Skip to main content

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