datafusion_common/
error.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! # Error Handling in DataFusion
19//!
20//! In DataFusion, there are two types of errors that can be raised:
21//!
22//! 1. Expected errors – These indicate invalid operations performed by the caller,
23//!    such as attempting to open a non-existent file. Different categories exist to
24//!    distinguish their sources (e.g., [`DataFusionError::ArrowError`],
25//!    [`DataFusionError::IoError`], etc.).
26//!
27//! 2. Unexpected errors – Represented by [`DataFusionError::Internal`], these
28//!    indicate that an internal invariant has been broken, suggesting a potential
29//!    bug in the system.
30//!
31//! There are several convenient macros for throwing errors. For example, use
32//! `exec_err!` for expected errors.
33//! For invariant checks, you can use `assert_or_internal_err!`,
34//! `assert_eq_or_internal_err!`, `assert_ne_or_internal_err!` for easier assertions.
35//! On the performance-critical path, use `debug_assert!` instead to reduce overhead.
36
37#[cfg(feature = "backtrace")]
38use std::backtrace::{Backtrace, BacktraceStatus};
39
40use std::borrow::Cow;
41use std::collections::VecDeque;
42use std::error::Error;
43use std::fmt::{Display, Formatter};
44use std::io;
45use std::result;
46use std::sync::Arc;
47
48use crate::utils::datafusion_strsim::normalized_levenshtein;
49use crate::utils::quote_identifier;
50use crate::{Column, DFSchema, Diagnostic, TableReference};
51#[cfg(feature = "avro")]
52use apache_avro::Error as AvroError;
53use arrow::error::ArrowError;
54#[cfg(feature = "parquet")]
55use parquet::errors::ParquetError;
56#[cfg(feature = "sql")]
57use sqlparser::parser::ParserError;
58use tokio::task::JoinError;
59
60/// Result type for operations that could result in an [DataFusionError]
61pub type Result<T, E = DataFusionError> = result::Result<T, E>;
62
63/// Result type for operations that could result in an [DataFusionError] and needs to be shared (wrapped into `Arc`).
64pub type SharedResult<T> = result::Result<T, Arc<DataFusionError>>;
65
66/// Error type for generic operations that could result in DataFusionError::External
67pub type GenericError = Box<dyn Error + Send + Sync>;
68
69/// DataFusion error
70#[derive(Debug)]
71pub enum DataFusionError {
72    /// Error returned by arrow.
73    ///
74    /// 2nd argument is for optional backtrace
75    ArrowError(Box<ArrowError>, Option<String>),
76    /// Error when reading / writing Parquet data.
77    #[cfg(feature = "parquet")]
78    ParquetError(Box<ParquetError>),
79    /// Error when reading Avro data.
80    #[cfg(feature = "avro")]
81    AvroError(Box<AvroError>),
82    /// Error when reading / writing to / from an object_store (e.g. S3 or LocalFile)
83    #[cfg(feature = "object_store")]
84    ObjectStore(Box<object_store::Error>),
85    /// Error when an I/O operation fails
86    IoError(io::Error),
87    /// Error when SQL is syntactically incorrect.
88    ///
89    /// 2nd argument is for optional backtrace
90    #[cfg(feature = "sql")]
91    SQL(Box<ParserError>, Option<String>),
92    /// Error when a feature is not yet implemented.
93    ///
94    /// These errors are sometimes returned for features that are still in
95    /// development and are not entirely complete. Often, these errors are
96    /// tracked in our issue tracker.
97    NotImplemented(String),
98    /// Error due to bugs in DataFusion
99    ///
100    /// This error should not happen in normal usage of DataFusion. It results
101    /// from something that wasn't expected/anticipated by the implementation
102    /// and that is most likely a bug (the error message even encourages users
103    /// to open a bug report). A user should not be able to trigger internal
104    /// errors under normal circumstances by feeding in malformed queries, bad
105    /// data, etc.
106    ///
107    /// Note that I/O errors (or any error that happens due to external systems)
108    /// do NOT fall under this category. See other variants such as
109    /// [`Self::IoError`] and [`Self::External`].
110    ///
111    /// DataFusions has internal invariants that the compiler is not always able
112    /// to check. This error is raised when one of those invariants does not
113    /// hold for some reason.
114    Internal(String),
115    /// Error during planning of the query.
116    ///
117    /// This error happens when the user provides a bad query or plan, for
118    /// example the user attempts to call a function that doesn't exist, or if
119    /// the types of a function call are not supported.
120    Plan(String),
121    /// Error for invalid or unsupported configuration options.
122    Configuration(String),
123    /// Error when there is a problem with the query related to schema.
124    ///
125    /// This error can be returned in cases such as when schema inference is not
126    /// possible and when column names are not unique.
127    ///
128    /// 2nd argument is for optional backtrace
129    /// Boxing the optional backtrace to prevent <https://rust-lang.github.io/rust-clippy/master/index.html#/result_large_err>
130    SchemaError(Box<SchemaError>, Box<Option<String>>),
131    /// Error during execution of the query.
132    ///
133    /// This error is returned when an error happens during execution due to a
134    /// malformed input. For example, the user passed malformed arguments to a
135    /// SQL method, opened a CSV file that is broken, or tried to divide an
136    /// integer by zero.
137    Execution(String),
138    /// [`JoinError`] during execution of the query.
139    ///
140    /// This error can't occur for unjoined tasks, such as execution shutdown.
141    ExecutionJoin(Box<JoinError>),
142    /// Error when resources (such as memory of scratch disk space) are exhausted.
143    ///
144    /// This error is thrown when a consumer cannot acquire additional memory
145    /// or other resources needed to execute the query from the Memory Manager.
146    ResourcesExhausted(String),
147    /// Errors originating from outside DataFusion's core codebase.
148    ///
149    /// For example, a custom S3Error from the crate datafusion-objectstore-s3
150    External(GenericError),
151    /// Error with additional context
152    Context(String, Box<DataFusionError>),
153    /// Errors from either mapping LogicalPlans to/from Substrait plans
154    /// or serializing/deserializing protobytes to Substrait plans
155    Substrait(String),
156    /// Error wrapped together with additional contextual information intended
157    /// for end users, to help them understand what went wrong by providing
158    /// human-readable messages, and locations in the source query that relate
159    /// to the error in some way.
160    Diagnostic(Box<Diagnostic>, Box<DataFusionError>),
161    /// A collection of one or more [`DataFusionError`]. Useful in cases where
162    /// DataFusion can recover from an erroneous state, and produce more errors
163    /// before terminating. e.g. when planning a SELECT clause, DataFusion can
164    /// synchronize to the next `SelectItem` if the previous one had errors. The
165    /// end result is that the user can see errors about all `SelectItem`,
166    /// instead of just the first one.
167    Collection(Vec<DataFusionError>),
168    /// A [`DataFusionError`] which shares an underlying [`DataFusionError`].
169    ///
170    /// This is useful when the same underlying [`DataFusionError`] is passed
171    /// to multiple receivers. For example, when the source of a repartition
172    /// errors and the error is propagated to multiple consumers.
173    Shared(Arc<DataFusionError>),
174    /// An error that originated during a foreign function interface call.
175    /// Transferring errors across the FFI boundary is difficult, so the original
176    /// error will be converted to a string.
177    Ffi(String),
178}
179
180#[macro_export]
181macro_rules! context {
182    ($desc:expr, $err:expr) => {
183        $err.context(format!("{} at {}:{}", $desc, file!(), line!()))
184    };
185}
186
187/// Schema-related errors
188#[derive(Debug)]
189pub enum SchemaError {
190    /// Schema contains a (possibly) qualified and unqualified field with same unqualified name
191    AmbiguousReference { field: Box<Column> },
192    /// Schema contains duplicate qualified field name
193    DuplicateQualifiedField {
194        qualifier: Box<TableReference>,
195        name: String,
196    },
197    /// Schema contains duplicate unqualified field name
198    DuplicateUnqualifiedField { name: String },
199    /// No field with this name
200    FieldNotFound {
201        field: Box<Column>,
202        valid_fields: Vec<Column>,
203    },
204}
205
206impl Display for SchemaError {
207    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208        match self {
209            Self::FieldNotFound {
210                field,
211                valid_fields,
212            } => {
213                write!(f, "No field named {}", field.quoted_flat_name())?;
214                let lower_valid_fields = valid_fields
215                    .iter()
216                    .map(|column| column.flat_name().to_lowercase())
217                    .collect::<Vec<String>>();
218
219                let valid_fields_names = valid_fields
220                    .iter()
221                    .map(|column| column.flat_name())
222                    .collect::<Vec<String>>();
223                if lower_valid_fields.contains(&field.flat_name().to_lowercase()) {
224                    write!(
225                        f,
226                        ". Column names are case sensitive. You can use double quotes to refer to the \"{}\" column \
227                        or set the datafusion.sql_parser.enable_ident_normalization configuration",
228                        field.quoted_flat_name()
229                    )?;
230                }
231                let field_name = field.name();
232                if let Some(matched) = valid_fields_names
233                    .iter()
234                    .filter(|str| normalized_levenshtein(str, field_name) >= 0.5)
235                    .collect::<Vec<&String>>()
236                    .first()
237                {
238                    write!(f, ". Did you mean '{matched}'?")?;
239                } else if !valid_fields.is_empty() {
240                    write!(
241                        f,
242                        ". Valid fields are {}",
243                        valid_fields
244                            .iter()
245                            .map(|field| field.quoted_flat_name())
246                            .collect::<Vec<String>>()
247                            .join(", ")
248                    )?;
249                }
250                write!(f, ".")
251            }
252            Self::DuplicateQualifiedField { qualifier, name } => {
253                write!(
254                    f,
255                    "Schema contains duplicate qualified field name {}.{}",
256                    qualifier.to_quoted_string(),
257                    quote_identifier(name)
258                )
259            }
260            Self::DuplicateUnqualifiedField { name } => {
261                write!(
262                    f,
263                    "Schema contains duplicate unqualified field name {}",
264                    quote_identifier(name)
265                )
266            }
267            Self::AmbiguousReference { field } => {
268                if field.relation.is_some() {
269                    write!(
270                        f,
271                        "Schema contains qualified field name {} and unqualified field name {} which would be ambiguous",
272                        field.quoted_flat_name(),
273                        quote_identifier(&field.name)
274                    )
275                } else {
276                    write!(
277                        f,
278                        "Ambiguous reference to unqualified field {}",
279                        field.quoted_flat_name()
280                    )
281                }
282            }
283        }
284    }
285}
286
287impl Error for SchemaError {}
288
289impl From<std::fmt::Error> for DataFusionError {
290    fn from(_e: std::fmt::Error) -> Self {
291        DataFusionError::Execution("Fail to format".to_string())
292    }
293}
294
295impl From<io::Error> for DataFusionError {
296    fn from(e: io::Error) -> Self {
297        DataFusionError::IoError(e)
298    }
299}
300
301impl From<ArrowError> for DataFusionError {
302    fn from(e: ArrowError) -> Self {
303        DataFusionError::ArrowError(Box::new(e), Some(DataFusionError::get_back_trace()))
304    }
305}
306
307impl From<DataFusionError> for ArrowError {
308    fn from(e: DataFusionError) -> Self {
309        match e {
310            DataFusionError::ArrowError(e, _) => *e,
311            DataFusionError::External(e) => ArrowError::ExternalError(e),
312            other => ArrowError::ExternalError(Box::new(other)),
313        }
314    }
315}
316
317impl From<&Arc<DataFusionError>> for DataFusionError {
318    fn from(e: &Arc<DataFusionError>) -> Self {
319        if let DataFusionError::Shared(e_inner) = e.as_ref() {
320            // don't re-wrap
321            DataFusionError::Shared(Arc::clone(e_inner))
322        } else {
323            DataFusionError::Shared(Arc::clone(e))
324        }
325    }
326}
327
328#[cfg(feature = "parquet")]
329impl From<ParquetError> for DataFusionError {
330    fn from(e: ParquetError) -> Self {
331        DataFusionError::ParquetError(Box::new(e))
332    }
333}
334
335#[cfg(feature = "avro")]
336impl From<AvroError> for DataFusionError {
337    fn from(e: AvroError) -> Self {
338        DataFusionError::AvroError(Box::new(e))
339    }
340}
341
342#[cfg(feature = "object_store")]
343impl From<object_store::Error> for DataFusionError {
344    fn from(e: object_store::Error) -> Self {
345        DataFusionError::ObjectStore(Box::new(e))
346    }
347}
348
349#[cfg(feature = "object_store")]
350impl From<object_store::path::Error> for DataFusionError {
351    fn from(e: object_store::path::Error) -> Self {
352        DataFusionError::ObjectStore(Box::new(e.into()))
353    }
354}
355
356#[cfg(feature = "sql")]
357impl From<ParserError> for DataFusionError {
358    fn from(e: ParserError) -> Self {
359        DataFusionError::SQL(Box::new(e), None)
360    }
361}
362
363impl From<GenericError> for DataFusionError {
364    fn from(err: GenericError) -> Self {
365        // If the error is already a DataFusionError, not wrapping it.
366        if err.is::<DataFusionError>() {
367            if let Ok(e) = err.downcast::<DataFusionError>() {
368                *e
369            } else {
370                unreachable!()
371            }
372        } else {
373            DataFusionError::External(err)
374        }
375    }
376}
377
378impl Display for DataFusionError {
379    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
380        let error_prefix = self.error_prefix();
381        let message = self.message();
382        write!(f, "{error_prefix}{message}")
383    }
384}
385
386impl Error for DataFusionError {
387    fn source(&self) -> Option<&(dyn Error + 'static)> {
388        match self {
389            DataFusionError::ArrowError(e, _) => Some(e.as_ref()),
390            #[cfg(feature = "parquet")]
391            DataFusionError::ParquetError(e) => Some(e.as_ref()),
392            #[cfg(feature = "avro")]
393            DataFusionError::AvroError(e) => Some(e.as_ref()),
394            #[cfg(feature = "object_store")]
395            DataFusionError::ObjectStore(e) => Some(e.as_ref()),
396            DataFusionError::IoError(e) => Some(e),
397            #[cfg(feature = "sql")]
398            DataFusionError::SQL(e, _) => Some(e.as_ref()),
399            DataFusionError::NotImplemented(_) => None,
400            DataFusionError::Internal(_) => None,
401            DataFusionError::Configuration(_) => None,
402            DataFusionError::Plan(_) => None,
403            DataFusionError::SchemaError(e, _) => Some(e.as_ref()),
404            DataFusionError::Execution(_) => None,
405            DataFusionError::ExecutionJoin(e) => Some(e.as_ref()),
406            DataFusionError::ResourcesExhausted(_) => None,
407            DataFusionError::External(e) => Some(e.as_ref()),
408            DataFusionError::Context(_, e) => Some(e.as_ref()),
409            DataFusionError::Substrait(_) => None,
410            DataFusionError::Diagnostic(_, e) => Some(e.as_ref()),
411            // Can't really make a Collection fit into the mold of "an error has
412            // at most one source", but returning the first one is probably good
413            // idea. Especially since `DataFusionError::Collection` is mostly
414            // meant for consumption by the end user, so shouldn't interfere
415            // with programmatic usage too much. Plus, having 1 or 5 errors
416            // doesn't really change the fact that the query is invalid and
417            // can't be executed.
418            DataFusionError::Collection(errs) => errs.first().map(|e| e as &dyn Error),
419            DataFusionError::Shared(e) => Some(e.as_ref()),
420            DataFusionError::Ffi(_) => None,
421        }
422    }
423}
424
425impl From<DataFusionError> for io::Error {
426    fn from(e: DataFusionError) -> Self {
427        io::Error::other(e)
428    }
429}
430
431impl DataFusionError {
432    /// The separator between the error message and the backtrace
433    pub const BACK_TRACE_SEP: &'static str = "\n\nbacktrace: ";
434
435    /// Get deepest underlying [`DataFusionError`]
436    ///
437    /// [`DataFusionError`]s sometimes form a chain, such as `DataFusionError::ArrowError()` in order to conform
438    /// to the correct error signature. Thus sometimes there is a chain several layers deep that can obscure the
439    /// original error. This function finds the lowest level DataFusionError possible.
440    ///
441    /// For example,  `find_root` will return`DataFusionError::ResourceExhausted` given the input
442    /// ```text
443    /// DataFusionError::ArrowError
444    ///   ArrowError::External
445    ///    Box(DataFusionError::Context)
446    ///      DataFusionError::ResourceExhausted
447    /// ```
448    ///
449    /// This may be the same as `self`.
450    pub fn find_root(&self) -> &Self {
451        // Note: This is a non-recursive algorithm so we do not run
452        // out of stack space, even for long error chains.
453
454        let mut last_datafusion_error = self;
455        let mut root_error: &dyn Error = self;
456        while let Some(source) = root_error.source() {
457            // walk the next level
458            root_error = source;
459            // remember the lowest datafusion error so far
460            if let Some(e) = root_error.downcast_ref::<DataFusionError>() {
461                last_datafusion_error = e;
462            } else if let Some(e) = root_error.downcast_ref::<Arc<DataFusionError>>() {
463                // As `Arc<T>::source()` calls through to `T::source()` we need to
464                // explicitly match `Arc<DataFusionError>` to capture it
465                last_datafusion_error = e.as_ref();
466            }
467        }
468        // return last checkpoint (which may be the original error)
469        last_datafusion_error
470    }
471
472    /// wraps self in Self::Context with a description
473    pub fn context(self, description: impl Into<String>) -> Self {
474        Self::Context(description.into(), Box::new(self))
475    }
476
477    /// Strips backtrace out of the error message
478    /// If backtrace enabled then error has a format "message" [`Self::BACK_TRACE_SEP`] "backtrace"
479    /// The method strips the backtrace and outputs "message"
480    pub fn strip_backtrace(&self) -> String {
481        (*self
482            .to_string()
483            .split(Self::BACK_TRACE_SEP)
484            .collect::<Vec<&str>>()
485            .first()
486            .unwrap_or(&""))
487        .to_string()
488    }
489
490    /// To enable optional rust backtrace in DataFusion:
491    /// - [`Setup Env Variables`]<https://doc.rust-lang.org/std/backtrace/index.html#environment-variables>
492    /// - Enable `backtrace` cargo feature
493    ///
494    /// Example:
495    /// cargo build --features 'backtrace'
496    /// RUST_BACKTRACE=1 ./app
497    #[inline(always)]
498    pub fn get_back_trace() -> String {
499        #[cfg(feature = "backtrace")]
500        {
501            let back_trace = Backtrace::capture();
502            if back_trace.status() == BacktraceStatus::Captured {
503                return format!("{}{}", Self::BACK_TRACE_SEP, back_trace);
504            }
505
506            "".to_owned()
507        }
508
509        #[cfg(not(feature = "backtrace"))]
510        "".to_owned()
511    }
512
513    /// Return a [`DataFusionErrorBuilder`] to build a [`DataFusionError`]
514    pub fn builder() -> DataFusionErrorBuilder {
515        DataFusionErrorBuilder::default()
516    }
517
518    fn error_prefix(&self) -> &'static str {
519        match self {
520            DataFusionError::ArrowError(_, _) => "Arrow error: ",
521            #[cfg(feature = "parquet")]
522            DataFusionError::ParquetError(_) => "Parquet error: ",
523            #[cfg(feature = "avro")]
524            DataFusionError::AvroError(_) => "Avro error: ",
525            #[cfg(feature = "object_store")]
526            DataFusionError::ObjectStore(_) => "Object Store error: ",
527            DataFusionError::IoError(_) => "IO error: ",
528            #[cfg(feature = "sql")]
529            DataFusionError::SQL(_, _) => "SQL error: ",
530            DataFusionError::NotImplemented(_) => {
531                "This feature is not implemented: "
532            }
533            DataFusionError::Internal(_) => "Internal error: ",
534            DataFusionError::Plan(_) => "Error during planning: ",
535            DataFusionError::Configuration(_) => {
536                "Invalid or Unsupported Configuration: "
537            }
538            DataFusionError::SchemaError(_, _) => "Schema error: ",
539            DataFusionError::Execution(_) => "Execution error: ",
540            DataFusionError::ExecutionJoin(_) => "ExecutionJoin error: ",
541            DataFusionError::ResourcesExhausted(_) => {
542                "Resources exhausted: "
543            }
544            DataFusionError::External(_) => "External error: ",
545            DataFusionError::Context(_, _) => "",
546            DataFusionError::Substrait(_) => "Substrait error: ",
547            DataFusionError::Diagnostic(_, _) => "",
548            DataFusionError::Collection(errs) => {
549                errs.first().expect("cannot construct DataFusionError::Collection with 0 errors, but got one such case").error_prefix()
550            }
551            DataFusionError::Shared(_) => "",
552            DataFusionError::Ffi(_) => "FFI error: ",
553        }
554    }
555
556    pub fn message(&self) -> Cow<'_, str> {
557        match *self {
558            DataFusionError::ArrowError(ref desc, ref backtrace) => {
559                let backtrace = backtrace.clone().unwrap_or_else(|| "".to_owned());
560                Cow::Owned(format!("{desc}{backtrace}"))
561            }
562            #[cfg(feature = "parquet")]
563            DataFusionError::ParquetError(ref desc) => Cow::Owned(desc.to_string()),
564            #[cfg(feature = "avro")]
565            DataFusionError::AvroError(ref desc) => Cow::Owned(desc.to_string()),
566            DataFusionError::IoError(ref desc) => Cow::Owned(desc.to_string()),
567            #[cfg(feature = "sql")]
568            DataFusionError::SQL(ref desc, ref backtrace) => {
569                let backtrace: String =
570                    backtrace.clone().unwrap_or_else(|| "".to_owned());
571                Cow::Owned(format!("{desc:?}{backtrace}"))
572            }
573            DataFusionError::Configuration(ref desc) => Cow::Owned(desc.to_string()),
574            DataFusionError::NotImplemented(ref desc) => Cow::Owned(desc.to_string()),
575            DataFusionError::Internal(ref desc) => Cow::Owned(format!(
576                "{desc}.\nThis issue was likely caused by a bug in DataFusion's code. \
577                Please help us to resolve this by filing a bug report in our issue tracker: \
578                https://github.com/apache/datafusion/issues"
579            )),
580            DataFusionError::Plan(ref desc) => Cow::Owned(desc.to_string()),
581            DataFusionError::SchemaError(ref desc, ref backtrace) => {
582                let backtrace: &str =
583                    &backtrace.as_ref().clone().unwrap_or_else(|| "".to_owned());
584                Cow::Owned(format!("{desc}{backtrace}"))
585            }
586            DataFusionError::Execution(ref desc) => Cow::Owned(desc.to_string()),
587            DataFusionError::ExecutionJoin(ref desc) => Cow::Owned(desc.to_string()),
588            DataFusionError::ResourcesExhausted(ref desc) => Cow::Owned(desc.to_string()),
589            DataFusionError::External(ref desc) => Cow::Owned(desc.to_string()),
590            #[cfg(feature = "object_store")]
591            DataFusionError::ObjectStore(ref desc) => Cow::Owned(desc.to_string()),
592            DataFusionError::Context(ref desc, ref err) => {
593                Cow::Owned(format!("{desc}\ncaused by\n{}", *err))
594            }
595            DataFusionError::Substrait(ref desc) => Cow::Owned(desc.to_string()),
596            DataFusionError::Diagnostic(_, ref err) => Cow::Owned(err.to_string()),
597            // Returning the message of the first error is probably fine enough,
598            // and makes `DataFusionError::Collection` a transparent wrapped,
599            // unless the end user explicitly calls `DataFusionError::iter`.
600            DataFusionError::Collection(ref errs) => errs
601                .first()
602                .expect("cannot construct DataFusionError::Collection with 0 errors")
603                .message(),
604            DataFusionError::Shared(ref desc) => Cow::Owned(desc.to_string()),
605            DataFusionError::Ffi(ref desc) => Cow::Owned(desc.to_string()),
606        }
607    }
608
609    /// Wraps the error with contextual information intended for end users
610    pub fn with_diagnostic(self, diagnostic: Diagnostic) -> Self {
611        Self::Diagnostic(Box::new(diagnostic), Box::new(self))
612    }
613
614    /// Wraps the error with contextual information intended for end users.
615    /// Takes a function that inspects the error and returns the diagnostic to
616    /// wrap it with.
617    pub fn with_diagnostic_fn<F: FnOnce(&DataFusionError) -> Diagnostic>(
618        self,
619        f: F,
620    ) -> Self {
621        let diagnostic = f(&self);
622        self.with_diagnostic(diagnostic)
623    }
624
625    /// Gets the [`Diagnostic`] associated with the error, if any. If there is
626    /// more than one, only the outermost [`Diagnostic`] is returned.
627    pub fn diagnostic(&self) -> Option<&Diagnostic> {
628        struct DiagnosticsIterator<'a> {
629            head: &'a DataFusionError,
630        }
631
632        impl<'a> Iterator for DiagnosticsIterator<'a> {
633            type Item = &'a Diagnostic;
634
635            fn next(&mut self) -> Option<Self::Item> {
636                loop {
637                    if let DataFusionError::Diagnostic(diagnostics, source) = self.head {
638                        self.head = source.as_ref();
639                        return Some(diagnostics);
640                    }
641
642                    if let Some(source) = self
643                        .head
644                        .source()
645                        .and_then(|source| source.downcast_ref::<DataFusionError>())
646                    {
647                        self.head = source;
648                    } else {
649                        return None;
650                    }
651                }
652            }
653        }
654
655        DiagnosticsIterator { head: self }.next()
656    }
657
658    /// Return an iterator over this [`DataFusionError`] and any other
659    /// [`DataFusionError`]s in a [`DataFusionError::Collection`].
660    ///
661    /// Sometimes DataFusion is able to collect multiple errors in a SQL query
662    /// before terminating, e.g. across different expressions in a SELECT
663    /// statements or different sides of a UNION. This method returns an
664    /// iterator over all the errors in the collection.
665    ///
666    /// For this to work, the top-level error must be a
667    /// `DataFusionError::Collection`, not something that contains it.
668    pub fn iter(&self) -> impl Iterator<Item = &DataFusionError> {
669        struct ErrorIterator<'a> {
670            queue: VecDeque<&'a DataFusionError>,
671        }
672
673        impl<'a> Iterator for ErrorIterator<'a> {
674            type Item = &'a DataFusionError;
675
676            fn next(&mut self) -> Option<Self::Item> {
677                loop {
678                    let popped = self.queue.pop_front()?;
679                    match popped {
680                        DataFusionError::Collection(errs) => self.queue.extend(errs),
681                        _ => return Some(popped),
682                    }
683                }
684            }
685        }
686
687        let mut queue = VecDeque::new();
688        queue.push_back(self);
689        ErrorIterator { queue }
690    }
691}
692
693/// A builder for [`DataFusionError`]
694///
695/// This builder can be used to collect multiple errors and return them as a
696/// [`DataFusionError::Collection`].
697///
698/// # Example: no errors
699/// ```
700/// # use datafusion_common::DataFusionError;
701/// let mut builder = DataFusionError::builder();
702/// // ok_or returns the value if no errors have been added
703/// assert_eq!(builder.error_or(42).unwrap(), 42);
704/// ```
705///
706/// # Example: with errors
707/// ```
708/// # use datafusion_common::{assert_contains, DataFusionError};
709/// let mut builder = DataFusionError::builder();
710/// builder.add_error(DataFusionError::Internal("foo".to_owned()));
711/// // ok_or returns the value if no errors have been added
712/// assert_contains!(
713///     builder.error_or(42).unwrap_err().to_string(),
714///     "Internal error: foo"
715/// );
716/// ```
717#[derive(Debug, Default)]
718pub struct DataFusionErrorBuilder(Vec<DataFusionError>);
719
720impl DataFusionErrorBuilder {
721    /// Create a new [`DataFusionErrorBuilder`]
722    pub fn new() -> Self {
723        Default::default()
724    }
725
726    /// Add an error to the in progress list
727    ///
728    /// # Example
729    /// ```
730    /// # use datafusion_common::{assert_contains, DataFusionError};
731    /// let mut builder = DataFusionError::builder();
732    /// builder.add_error(DataFusionError::Internal("foo".to_owned()));
733    /// assert_contains!(
734    ///     builder.error_or(42).unwrap_err().to_string(),
735    ///     "Internal error: foo"
736    /// );
737    /// ```
738    pub fn add_error(&mut self, error: DataFusionError) {
739        self.0.push(error);
740    }
741
742    /// Add an error to the in progress list, returning the builder
743    ///
744    /// # Example
745    /// ```
746    /// # use datafusion_common::{assert_contains, DataFusionError};
747    /// let builder = DataFusionError::builder()
748    ///     .with_error(DataFusionError::Internal("foo".to_owned()));
749    /// assert_contains!(
750    ///     builder.error_or(42).unwrap_err().to_string(),
751    ///     "Internal error: foo"
752    /// );
753    /// ```
754    pub fn with_error(mut self, error: DataFusionError) -> Self {
755        self.0.push(error);
756        self
757    }
758
759    /// Returns `Ok(ok)` if no errors were added to the builder,
760    /// otherwise returns a `Result::Err`
761    pub fn error_or<T>(self, ok: T) -> Result<T, DataFusionError> {
762        match self.0.len() {
763            0 => Ok(ok),
764            1 => Err(self.0.into_iter().next().expect("length matched 1")),
765            _ => Err(DataFusionError::Collection(self.0)),
766        }
767    }
768}
769
770/// Unwrap an `Option` if possible. Otherwise return an `DataFusionError::Internal`.
771/// In normal usage of DataFusion the unwrap should always succeed.
772///
773/// Example: `let values = unwrap_or_internal_err!(values)`
774#[macro_export]
775macro_rules! unwrap_or_internal_err {
776    ($Value: ident) => {
777        $Value.ok_or_else(|| {
778            $crate::DataFusionError::Internal(format!(
779                "{} should not be None",
780                stringify!($Value)
781            ))
782        })?
783    };
784}
785
786/// Assert a condition, returning `DataFusionError::Internal` on failure.
787///
788/// # Examples
789///
790/// ```text
791/// assert_or_internal_err!(predicate);
792/// assert_or_internal_err!(predicate, "human readable message");
793/// assert_or_internal_err!(predicate, format!("details: {}", value));
794/// ```
795#[macro_export]
796macro_rules! assert_or_internal_err {
797    ($cond:expr) => {
798        if !$cond {
799            return Err($crate::DataFusionError::Internal(format!(
800                "Assertion failed: {}",
801                stringify!($cond)
802            )));
803        }
804    };
805    ($cond:expr, $($arg:tt)+) => {
806        if !$cond {
807            return Err($crate::DataFusionError::Internal(format!(
808                "Assertion failed: {}: {}",
809                stringify!($cond),
810                format!($($arg)+)
811            )));
812        }
813    };
814}
815
816/// Assert equality, returning `DataFusionError::Internal` on failure.
817///
818/// # Examples
819///
820/// ```text
821/// assert_eq_or_internal_err!(actual, expected);
822/// assert_eq_or_internal_err!(left_expr, right_expr, "values must match");
823/// assert_eq_or_internal_err!(lhs, rhs, "metadata: {}", extra);
824/// ```
825#[macro_export]
826macro_rules! assert_eq_or_internal_err {
827    ($left:expr, $right:expr $(,)?) => {{
828        let left_val = &$left;
829        let right_val = &$right;
830        if left_val != right_val {
831            return Err($crate::DataFusionError::Internal(format!(
832                "Assertion failed: {} == {} (left: {:?}, right: {:?})",
833                stringify!($left),
834                stringify!($right),
835                left_val,
836                right_val
837            )));
838        }
839    }};
840    ($left:expr, $right:expr, $($arg:tt)+) => {{
841        let left_val = &$left;
842        let right_val = &$right;
843        if left_val != right_val {
844            return Err($crate::DataFusionError::Internal(format!(
845                "Assertion failed: {} == {} (left: {:?}, right: {:?}): {}",
846                stringify!($left),
847                stringify!($right),
848                left_val,
849                right_val,
850                format!($($arg)+)
851            )));
852        }
853    }};
854}
855
856/// Assert inequality, returning `DataFusionError::Internal` on failure.
857///
858/// # Examples
859///
860/// ```text
861/// assert_ne_or_internal_err!(left, right);
862/// assert_ne_or_internal_err!(lhs_expr, rhs_expr, "values must differ");
863/// assert_ne_or_internal_err!(a, b, "context {}", info);
864/// ```
865#[macro_export]
866macro_rules! assert_ne_or_internal_err {
867    ($left:expr, $right:expr $(,)?) => {{
868        let left_val = &$left;
869        let right_val = &$right;
870        if left_val == right_val {
871            return Err($crate::DataFusionError::Internal(format!(
872                "Assertion failed: {} != {} (left: {:?}, right: {:?})",
873                stringify!($left),
874                stringify!($right),
875                left_val,
876                right_val
877            )));
878        }
879    }};
880    ($left:expr, $right:expr, $($arg:tt)+) => {{
881        let left_val = &$left;
882        let right_val = &$right;
883        if left_val == right_val {
884            return Err($crate::DataFusionError::Internal(format!(
885                "Assertion failed: {} != {} (left: {:?}, right: {:?}): {}",
886                stringify!($left),
887                stringify!($right),
888                left_val,
889                right_val,
890                format!($($arg)+)
891            )));
892        }
893    }};
894}
895
896/// Add a macros for concise  DataFusionError::* errors declaration
897/// supports placeholders the same way as `format!`
898/// Examples:
899///     plan_err!("Error")
900///     plan_err!("Error {}", val)
901///     plan_err!("Error {:?}", val)
902///     plan_err!("Error {val}")
903///     plan_err!("Error {val:?}")
904///
905/// `NAME_ERR` -  macro name for wrapping Err(DataFusionError::*)
906/// `NAME_DF_ERR` -  macro name for wrapping DataFusionError::*. Needed to keep backtrace opportunity
907/// in construction where DataFusionError::* used directly, like `map_err`, `ok_or_else`, etc
908macro_rules! make_error {
909    ($NAME_ERR:ident, $NAME_DF_ERR: ident, $ERR:ident) => { make_error!(@inner ($), $NAME_ERR, $NAME_DF_ERR, $ERR); };
910    (@inner ($d:tt), $NAME_ERR:ident, $NAME_DF_ERR:ident, $ERR:ident) => {
911        ::paste::paste!{
912            /// Macro wraps `$ERR` to add backtrace feature
913            #[macro_export]
914            macro_rules! $NAME_DF_ERR {
915                ($d($d args:expr),* $d(; diagnostic=$d DIAG:expr)?) => {{
916                    let err =$crate::DataFusionError::$ERR(
917                        ::std::format!(
918                            "{}{}",
919                            ::std::format!($d($d args),*),
920                            $crate::DataFusionError::get_back_trace(),
921                        ).into()
922                    );
923                    $d (
924                        let err = err.with_diagnostic($d DIAG);
925                    )?
926                    err
927                }
928            }
929        }
930
931            /// Macro wraps Err(`$ERR`) to add backtrace feature
932            #[macro_export]
933            macro_rules! $NAME_ERR {
934                ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
935                    let err = $crate::[<_ $NAME_DF_ERR>]!($d($d args),*);
936                    $d (
937                        let err = err.with_diagnostic($d DIAG);
938                    )?
939                    Err(err)
940
941                }}
942            }
943
944
945            #[doc(hidden)]
946            pub use $NAME_ERR as [<_ $NAME_ERR>];
947            #[doc(hidden)]
948            pub use $NAME_DF_ERR as [<_ $NAME_DF_ERR>];
949        }
950    };
951}
952
953// Exposes a macro to create `DataFusionError::Plan` with optional backtrace
954make_error!(plan_err, plan_datafusion_err, Plan);
955
956// Exposes a macro to create `DataFusionError::Internal` with optional backtrace
957make_error!(internal_err, internal_datafusion_err, Internal);
958
959// Exposes a macro to create `DataFusionError::NotImplemented` with optional backtrace
960make_error!(not_impl_err, not_impl_datafusion_err, NotImplemented);
961
962// Exposes a macro to create `DataFusionError::Execution` with optional backtrace
963make_error!(exec_err, exec_datafusion_err, Execution);
964
965// Exposes a macro to create `DataFusionError::Configuration` with optional backtrace
966make_error!(config_err, config_datafusion_err, Configuration);
967
968// Exposes a macro to create `DataFusionError::Substrait` with optional backtrace
969make_error!(substrait_err, substrait_datafusion_err, Substrait);
970
971// Exposes a macro to create `DataFusionError::ResourcesExhausted` with optional backtrace
972make_error!(resources_err, resources_datafusion_err, ResourcesExhausted);
973
974// Exposes a macro to create `DataFusionError::Ffi` with optional backtrace
975make_error!(ffi_err, ffi_datafusion_err, Ffi);
976
977// Exposes a macro to create `DataFusionError::SQL` with optional backtrace
978#[macro_export]
979macro_rules! sql_datafusion_err {
980    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
981        let err = $crate::DataFusionError::SQL(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
982        $(
983            let err = err.with_diagnostic($DIAG);
984        )?
985        err
986    }};
987}
988
989// Exposes a macro to create `Err(DataFusionError::SQL)` with optional backtrace
990#[macro_export]
991macro_rules! sql_err {
992    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
993        let err = $crate::sql_datafusion_err!($ERR);
994        $(
995            let err = err.with_diagnostic($DIAG);
996        )?
997        Err(err)
998    }};
999}
1000
1001// Exposes a macro to create `DataFusionError::ArrowError` with optional backtrace
1002#[macro_export]
1003macro_rules! arrow_datafusion_err {
1004    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1005        let err = $crate::DataFusionError::ArrowError(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1006        $(
1007            let err = err.with_diagnostic($DIAG);
1008        )?
1009        err
1010    }};
1011}
1012
1013// Exposes a macro to create `Err(DataFusionError::ArrowError)` with optional backtrace
1014#[macro_export]
1015macro_rules! arrow_err {
1016    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {
1017    {
1018        let err = $crate::arrow_datafusion_err!($ERR);
1019        $(
1020            let err = err.with_diagnostic($DIAG);
1021        )?
1022        Err(err)
1023    }};
1024}
1025
1026// Exposes a macro to create `DataFusionError::SchemaError` with optional backtrace
1027#[macro_export]
1028macro_rules! schema_datafusion_err {
1029    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1030        let err = $crate::DataFusionError::SchemaError(
1031            Box::new($ERR),
1032            Box::new(Some($crate::DataFusionError::get_back_trace())),
1033        );
1034        $(
1035            let err = err.with_diagnostic($DIAG);
1036        )?
1037        err
1038    }};
1039}
1040
1041// Exposes a macro to create `Err(DataFusionError::SchemaError)` with optional backtrace
1042#[macro_export]
1043macro_rules! schema_err {
1044    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1045        let err = $crate::DataFusionError::SchemaError(
1046            Box::new($ERR),
1047            Box::new(Some($crate::DataFusionError::get_back_trace())),
1048        );
1049        $(
1050            let err = err.with_diagnostic($DIAG);
1051        )?
1052        Err(err)
1053    }
1054    };
1055}
1056
1057// To avoid compiler error when using macro in the same crate:
1058// macros from the current crate cannot be referred to by absolute paths
1059pub use schema_err as _schema_err;
1060
1061/// Create a "field not found" DataFusion::SchemaError
1062pub fn field_not_found<R: Into<TableReference>>(
1063    qualifier: Option<R>,
1064    name: &str,
1065    schema: &DFSchema,
1066) -> DataFusionError {
1067    schema_datafusion_err!(SchemaError::FieldNotFound {
1068        field: Box::new(Column::new(qualifier, name)),
1069        valid_fields: schema.columns().to_vec(),
1070    })
1071}
1072
1073/// Convenience wrapper over [`field_not_found`] for when there is no qualifier
1074pub fn unqualified_field_not_found(name: &str, schema: &DFSchema) -> DataFusionError {
1075    schema_datafusion_err!(SchemaError::FieldNotFound {
1076        field: Box::new(Column::new_unqualified(name)),
1077        valid_fields: schema.columns().to_vec(),
1078    })
1079}
1080
1081pub fn add_possible_columns_to_diag(
1082    diagnostic: &mut Diagnostic,
1083    field: &Column,
1084    valid_fields: &[Column],
1085) {
1086    let field_names: Vec<String> = valid_fields
1087        .iter()
1088        .filter_map(|f| {
1089            if normalized_levenshtein(f.name(), field.name()) >= 0.5 {
1090                Some(f.flat_name())
1091            } else {
1092                None
1093            }
1094        })
1095        .collect();
1096
1097    for name in field_names {
1098        diagnostic.add_note(format!("possible column {name}"), None);
1099    }
1100}
1101
1102#[cfg(test)]
1103mod test {
1104    use super::*;
1105
1106    use std::mem::size_of;
1107    use std::sync::Arc;
1108
1109    use arrow::error::ArrowError;
1110    use insta::assert_snapshot;
1111
1112    fn ok_result() -> Result<()> {
1113        Ok(())
1114    }
1115
1116    #[test]
1117    fn test_assert_eq_or_internal_err_passes() -> Result<()> {
1118        assert_eq_or_internal_err!(1, 1);
1119        ok_result()
1120    }
1121
1122    #[test]
1123    fn test_assert_eq_or_internal_err_fails() {
1124        fn check() -> Result<()> {
1125            assert_eq_or_internal_err!(1, 2, "expected equality");
1126            ok_result()
1127        }
1128
1129        let err = check().unwrap_err();
1130        assert_snapshot!(
1131            err.to_string(),
1132            @r"
1133        Internal error: Assertion failed: 1 == 2 (left: 1, right: 2): expected equality.
1134        This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1135        "
1136        );
1137    }
1138
1139    #[test]
1140    fn test_assert_ne_or_internal_err_passes() -> Result<()> {
1141        assert_ne_or_internal_err!(1, 2);
1142        ok_result()
1143    }
1144
1145    #[test]
1146    fn test_assert_ne_or_internal_err_fails() {
1147        fn check() -> Result<()> {
1148            assert_ne_or_internal_err!(3, 3, "values must differ");
1149            ok_result()
1150        }
1151
1152        let err = check().unwrap_err();
1153        assert_snapshot!(
1154            err.to_string(),
1155            @r"
1156        Internal error: Assertion failed: 3 != 3 (left: 3, right: 3): values must differ.
1157        This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1158        "
1159        );
1160    }
1161
1162    #[test]
1163    fn test_assert_or_internal_err_passes() -> Result<()> {
1164        assert_or_internal_err!(true);
1165        assert_or_internal_err!(true, "message");
1166        ok_result()
1167    }
1168
1169    #[test]
1170    fn test_assert_or_internal_err_fails_default() {
1171        fn check() -> Result<()> {
1172            assert_or_internal_err!(false);
1173            ok_result()
1174        }
1175
1176        let err = check().unwrap_err();
1177        assert_snapshot!(
1178            err.to_string(),
1179            @r"
1180        Internal error: Assertion failed: false.
1181        This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1182        "
1183        );
1184    }
1185
1186    #[test]
1187    fn test_assert_or_internal_err_fails_with_message() {
1188        fn check() -> Result<()> {
1189            assert_or_internal_err!(false, "custom message");
1190            ok_result()
1191        }
1192
1193        let err = check().unwrap_err();
1194        assert_snapshot!(
1195            err.to_string(),
1196            @r"
1197        Internal error: Assertion failed: false: custom message.
1198        This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1199        "
1200        );
1201    }
1202
1203    #[test]
1204    fn test_assert_or_internal_err_with_format_arguments() {
1205        fn check() -> Result<()> {
1206            assert_or_internal_err!(false, "custom {}", 42);
1207            ok_result()
1208        }
1209
1210        let err = check().unwrap_err();
1211        assert_snapshot!(
1212            err.to_string(),
1213            @r"
1214        Internal error: Assertion failed: false: custom 42.
1215        This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1216        "
1217        );
1218    }
1219
1220    #[test]
1221    fn test_error_size() {
1222        // Since Errors influence the size of Result which influence the size of the stack
1223        // please don't allow this to grow larger
1224        assert_eq!(size_of::<SchemaError>(), 40);
1225        assert_eq!(size_of::<DataFusionError>(), 40);
1226    }
1227
1228    #[test]
1229    fn datafusion_error_to_arrow() {
1230        let res = return_arrow_error().unwrap_err();
1231        assert!(
1232            res.to_string()
1233                .starts_with("External error: Error during planning: foo")
1234        );
1235    }
1236
1237    #[test]
1238    fn arrow_error_to_datafusion() {
1239        let res = return_datafusion_error().unwrap_err();
1240        assert_eq!(res.strip_backtrace(), "Arrow error: Schema error: bar");
1241    }
1242
1243    // To pass the test the environment variable RUST_BACKTRACE should be set to 1 to enforce backtrace
1244    #[cfg(feature = "backtrace")]
1245    #[test]
1246    #[expect(clippy::unnecessary_literal_unwrap)]
1247    fn test_enabled_backtrace() {
1248        match std::env::var("RUST_BACKTRACE") {
1249            Ok(val) if val == "1" => {}
1250            _ => panic!("Environment variable RUST_BACKTRACE must be set to 1"),
1251        };
1252
1253        let res: Result<(), DataFusionError> = plan_err!("Err");
1254        let err = res.unwrap_err().to_string();
1255        assert!(err.contains(DataFusionError::BACK_TRACE_SEP));
1256        assert_eq!(
1257            err.split(DataFusionError::BACK_TRACE_SEP)
1258                .collect::<Vec<&str>>()
1259                .first()
1260                .unwrap(),
1261            &"Error during planning: Err"
1262        );
1263        assert!(
1264            !err.split(DataFusionError::BACK_TRACE_SEP)
1265                .collect::<Vec<&str>>()
1266                .get(1)
1267                .unwrap()
1268                .is_empty()
1269        );
1270    }
1271
1272    #[cfg(not(feature = "backtrace"))]
1273    #[test]
1274    fn test_disabled_backtrace() {
1275        let res: Result<(), DataFusionError> = plan_err!("Err");
1276        let res = res.unwrap_err().to_string();
1277        assert!(!res.contains(DataFusionError::BACK_TRACE_SEP));
1278        assert_eq!(res, "Error during planning: Err");
1279    }
1280
1281    #[test]
1282    fn test_find_root_error() {
1283        do_root_test(
1284            DataFusionError::Context(
1285                "it happened!".to_string(),
1286                Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1287            ),
1288            DataFusionError::ResourcesExhausted("foo".to_string()),
1289        );
1290
1291        do_root_test(
1292            DataFusionError::ArrowError(
1293                Box::new(ArrowError::ExternalError(Box::new(
1294                    DataFusionError::ResourcesExhausted("foo".to_string()),
1295                ))),
1296                None,
1297            ),
1298            DataFusionError::ResourcesExhausted("foo".to_string()),
1299        );
1300
1301        do_root_test(
1302            DataFusionError::External(Box::new(DataFusionError::ResourcesExhausted(
1303                "foo".to_string(),
1304            ))),
1305            DataFusionError::ResourcesExhausted("foo".to_string()),
1306        );
1307
1308        do_root_test(
1309            DataFusionError::External(Box::new(ArrowError::ExternalError(Box::new(
1310                DataFusionError::ResourcesExhausted("foo".to_string()),
1311            )))),
1312            DataFusionError::ResourcesExhausted("foo".to_string()),
1313        );
1314
1315        do_root_test(
1316            DataFusionError::ArrowError(
1317                Box::new(ArrowError::ExternalError(Box::new(
1318                    ArrowError::ExternalError(Box::new(
1319                        DataFusionError::ResourcesExhausted("foo".to_string()),
1320                    )),
1321                ))),
1322                None,
1323            ),
1324            DataFusionError::ResourcesExhausted("foo".to_string()),
1325        );
1326
1327        do_root_test(
1328            DataFusionError::External(Box::new(Arc::new(
1329                DataFusionError::ResourcesExhausted("foo".to_string()),
1330            ))),
1331            DataFusionError::ResourcesExhausted("foo".to_string()),
1332        );
1333
1334        do_root_test(
1335            DataFusionError::External(Box::new(Arc::new(ArrowError::ExternalError(
1336                Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1337            )))),
1338            DataFusionError::ResourcesExhausted("foo".to_string()),
1339        );
1340    }
1341
1342    #[test]
1343    fn test_make_error_parse_input() {
1344        let res: Result<(), DataFusionError> = plan_err!("Err");
1345        let res = res.unwrap_err();
1346        assert_eq!(res.strip_backtrace(), "Error during planning: Err");
1347
1348        let extra1 = "extra1";
1349        let extra2 = "extra2";
1350
1351        let res: Result<(), DataFusionError> = plan_err!("Err {} {}", extra1, extra2);
1352        let res = res.unwrap_err();
1353        assert_eq!(
1354            res.strip_backtrace(),
1355            "Error during planning: Err extra1 extra2"
1356        );
1357
1358        let res: Result<(), DataFusionError> =
1359            plan_err!("Err {:?} {:#?}", extra1, extra2);
1360        let res = res.unwrap_err();
1361        assert_eq!(
1362            res.strip_backtrace(),
1363            "Error during planning: Err \"extra1\" \"extra2\""
1364        );
1365
1366        let res: Result<(), DataFusionError> = plan_err!("Err {extra1} {extra2}");
1367        let res = res.unwrap_err();
1368        assert_eq!(
1369            res.strip_backtrace(),
1370            "Error during planning: Err extra1 extra2"
1371        );
1372
1373        let res: Result<(), DataFusionError> = plan_err!("Err {extra1:?} {extra2:#?}");
1374        let res = res.unwrap_err();
1375        assert_eq!(
1376            res.strip_backtrace(),
1377            "Error during planning: Err \"extra1\" \"extra2\""
1378        );
1379    }
1380
1381    #[test]
1382    fn external_error() {
1383        // assert not wrapping DataFusionError
1384        let generic_error: GenericError =
1385            Box::new(DataFusionError::Plan("test".to_string()));
1386        let datafusion_error: DataFusionError = generic_error.into();
1387        println!("{}", datafusion_error.strip_backtrace());
1388        assert_eq!(
1389            datafusion_error.strip_backtrace(),
1390            "Error during planning: test"
1391        );
1392
1393        // assert wrapping other Error
1394        let generic_error: GenericError = Box::new(io::Error::other("io error"));
1395        let datafusion_error: DataFusionError = generic_error.into();
1396        println!("{}", datafusion_error.strip_backtrace());
1397        assert_eq!(
1398            datafusion_error.strip_backtrace(),
1399            "External error: io error"
1400        );
1401    }
1402
1403    #[test]
1404    fn external_error_no_recursive() {
1405        let generic_error_1: GenericError = Box::new(io::Error::other("io error"));
1406        let external_error_1: DataFusionError = generic_error_1.into();
1407        let generic_error_2: GenericError = Box::new(external_error_1);
1408        let external_error_2: DataFusionError = generic_error_2.into();
1409
1410        println!("{external_error_2}");
1411        assert!(
1412            external_error_2
1413                .to_string()
1414                .starts_with("External error: io error")
1415        );
1416    }
1417
1418    /// Model what happens when implementing SendableRecordBatchStream:
1419    /// DataFusion code needs to return an ArrowError
1420    fn return_arrow_error() -> arrow::error::Result<()> {
1421        // Expect the '?' to work
1422        Err(DataFusionError::Plan("foo".to_string()).into())
1423    }
1424
1425    /// Model what happens when using arrow kernels in DataFusion
1426    /// code: need to turn an ArrowError into a DataFusionError
1427    fn return_datafusion_error() -> Result<()> {
1428        // Expect the '?' to work
1429        Err(ArrowError::SchemaError("bar".to_string()).into())
1430    }
1431
1432    fn do_root_test(e: DataFusionError, exp: DataFusionError) {
1433        let e = e.find_root();
1434
1435        // DataFusionError does not implement Eq, so we use a string comparison + some cheap "same variant" test instead
1436        assert_eq!(e.strip_backtrace(), exp.strip_backtrace());
1437        assert_eq!(std::mem::discriminant(e), std::mem::discriminant(&exp),)
1438    }
1439
1440    #[test]
1441    fn test_iter() {
1442        let err = DataFusionError::Collection(vec![
1443            DataFusionError::Plan("a".to_string()),
1444            DataFusionError::Collection(vec![
1445                DataFusionError::Plan("b".to_string()),
1446                DataFusionError::Plan("c".to_string()),
1447            ]),
1448        ]);
1449        let errs = err.iter().collect::<Vec<_>>();
1450        assert_eq!(errs.len(), 3);
1451        assert_eq!(errs[0].strip_backtrace(), "Error during planning: a");
1452        assert_eq!(errs[1].strip_backtrace(), "Error during planning: b");
1453        assert_eq!(errs[2].strip_backtrace(), "Error during planning: c");
1454    }
1455}