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::{levenshtein, 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
201fn case_insensitive_field_match<'a>(
202    field: &Column,
203    valid_fields: &'a [Column],
204) -> Option<&'a Column> {
205    let field_name = field.name();
206    let field_flat_name = field.flat_name();
207    let field_name_lower = field_name.to_lowercase();
208    let field_flat_name_lower = field_flat_name.to_lowercase();
209
210    valid_fields.iter().find(|valid_field| {
211        let valid_field_name = valid_field.name();
212        let valid_field_flat_name = valid_field.flat_name();
213        let valid_field_name_lower = valid_field_name.to_lowercase();
214        let valid_field_flat_name_lower = valid_field_flat_name.to_lowercase();
215
216        let name_differs_only_by_case =
217            field_name_lower == valid_field_name_lower && field_name != valid_field_name;
218        let flat_name_differs_only_by_case = field_flat_name_lower
219            == valid_field_flat_name_lower
220            && field_flat_name != valid_field_flat_name;
221
222        name_differs_only_by_case || flat_name_differs_only_by_case
223    })
224}
225
226/// Find the most similar field name based on edit distance.
227/// Returns `None` if all candidate edit distances are too far away.
228fn closest_valid_field<'a>(
229    field: &Column,
230    valid_fields: &'a [Column],
231) -> Option<&'a Column> {
232    // Find the most similar valid field name.
233    let target_names = [
234        field.name().to_lowercase(),
235        field.flat_name().to_lowercase(),
236    ];
237
238    let mut best_match: Option<(usize, usize, usize, &Column)> = None;
239    for (index, valid_field) in valid_fields.iter().enumerate() {
240        let valid_names = [
241            valid_field.name().to_lowercase(),
242            valid_field.flat_name().to_lowercase(),
243        ];
244        for target in &target_names {
245            for valid_name in &valid_names {
246                let distance = levenshtein(target, valid_name);
247                let max_len = target.chars().count().max(valid_name.chars().count());
248                // If there are no shared characters, or we would have to edit
249                // more than half of the longer name, don't suggest a potential match.
250                if max_len == 0 || distance * 2 > max_len {
251                    continue;
252                }
253
254                let should_replace = best_match.is_none_or(
255                    |(best_distance, best_max_len, best_index, _)| {
256                        distance < best_distance
257                            || distance == best_distance
258                                && (max_len > best_max_len
259                                    || max_len == best_max_len && index < best_index)
260                    },
261                );
262                if should_replace {
263                    best_match = Some((distance, max_len, index, valid_field));
264                }
265            }
266        }
267    }
268
269    best_match.map(|(_, _, _, valid_field)| valid_field)
270}
271
272impl Display for SchemaError {
273    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
274        match self {
275            Self::FieldNotFound {
276                field,
277                valid_fields,
278            } => {
279                let closest_field = closest_valid_field(field, valid_fields);
280                let case_sensitive_match =
281                    case_insensitive_field_match(field, valid_fields);
282
283                write!(f, "No field named {}", field.quoted_flat_name())?;
284                if let Some(matched) = closest_field {
285                    write!(f, ". Did you mean '{}'?", matched.quoted_flat_name())?;
286                } else {
287                    write!(f, ".")?;
288                }
289
290                if let Some(case_sensitive_match) = case_sensitive_match {
291                    write!(
292                        f,
293                        "\nColumn names are case sensitive. You can use double quotes to refer to the {} column \
294                        or disable the datafusion.sql_parser.enable_ident_normalization configuration.",
295                        case_sensitive_match.quoted_flat_name()
296                    )?;
297                }
298
299                if !valid_fields.is_empty() {
300                    write!(
301                        f,
302                        "\nValid fields are {}.",
303                        valid_fields
304                            .iter()
305                            .map(|field| field.quoted_flat_name())
306                            .collect::<Vec<String>>()
307                            .join(", ")
308                    )
309                } else {
310                    Ok(())
311                }
312            }
313            Self::DuplicateQualifiedField { qualifier, name } => {
314                write!(
315                    f,
316                    "Schema contains duplicate qualified field name {}.{}",
317                    qualifier.to_quoted_string(),
318                    quote_identifier(name)
319                )
320            }
321            Self::DuplicateUnqualifiedField { name } => {
322                write!(
323                    f,
324                    "Schema contains duplicate unqualified field name {}",
325                    quote_identifier(name)
326                )
327            }
328            Self::AmbiguousReference { field } => {
329                if field.relation.is_some() {
330                    write!(
331                        f,
332                        "Schema contains qualified field name {} and unqualified field name {} which would be ambiguous",
333                        field.quoted_flat_name(),
334                        quote_identifier(&field.name)
335                    )
336                } else {
337                    write!(
338                        f,
339                        "Ambiguous reference to unqualified field {}",
340                        field.quoted_flat_name()
341                    )
342                }
343            }
344        }
345    }
346}
347
348impl Error for SchemaError {}
349
350impl From<std::fmt::Error> for DataFusionError {
351    fn from(_e: std::fmt::Error) -> Self {
352        DataFusionError::Execution("Fail to format".to_string())
353    }
354}
355
356impl From<io::Error> for DataFusionError {
357    fn from(e: io::Error) -> Self {
358        DataFusionError::IoError(e)
359    }
360}
361
362impl From<ArrowError> for DataFusionError {
363    fn from(e: ArrowError) -> Self {
364        DataFusionError::ArrowError(Box::new(e), Some(DataFusionError::get_back_trace()))
365    }
366}
367
368impl From<DataFusionError> for ArrowError {
369    fn from(e: DataFusionError) -> Self {
370        match e {
371            DataFusionError::ArrowError(e, _) => *e,
372            DataFusionError::External(e) => ArrowError::ExternalError(e),
373            other => ArrowError::ExternalError(Box::new(other)),
374        }
375    }
376}
377
378impl From<&Arc<DataFusionError>> for DataFusionError {
379    fn from(e: &Arc<DataFusionError>) -> Self {
380        if let DataFusionError::Shared(e_inner) = e.as_ref() {
381            // don't re-wrap
382            DataFusionError::Shared(Arc::clone(e_inner))
383        } else {
384            DataFusionError::Shared(Arc::clone(e))
385        }
386    }
387}
388
389#[cfg(feature = "parquet")]
390impl From<ParquetError> for DataFusionError {
391    fn from(e: ParquetError) -> Self {
392        DataFusionError::ParquetError(Box::new(e))
393    }
394}
395
396#[cfg(feature = "object_store")]
397impl From<object_store::Error> for DataFusionError {
398    fn from(e: object_store::Error) -> Self {
399        DataFusionError::ObjectStore(Box::new(e))
400    }
401}
402
403#[cfg(feature = "object_store")]
404impl From<object_store::path::Error> for DataFusionError {
405    fn from(e: object_store::path::Error) -> Self {
406        DataFusionError::ObjectStore(Box::new(e.into()))
407    }
408}
409
410#[cfg(feature = "sql")]
411impl From<ParserError> for DataFusionError {
412    fn from(e: ParserError) -> Self {
413        DataFusionError::SQL(Box::new(e), None)
414    }
415}
416
417impl From<GenericError> for DataFusionError {
418    fn from(err: GenericError) -> Self {
419        // If the error is already a DataFusionError, not wrapping it.
420        if err.is::<DataFusionError>() {
421            if let Ok(e) = err.downcast::<DataFusionError>() {
422                *e
423            } else {
424                unreachable!()
425            }
426        } else {
427            DataFusionError::External(err)
428        }
429    }
430}
431
432impl Display for DataFusionError {
433    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
434        let error_prefix = self.error_prefix();
435        let message = self.message();
436        write!(f, "{error_prefix}{message}")
437    }
438}
439
440impl Error for DataFusionError {
441    fn source(&self) -> Option<&(dyn Error + 'static)> {
442        match self {
443            DataFusionError::ArrowError(e, _) => Some(e.as_ref()),
444            #[cfg(feature = "parquet")]
445            DataFusionError::ParquetError(e) => Some(e.as_ref()),
446            #[cfg(feature = "object_store")]
447            DataFusionError::ObjectStore(e) => Some(e.as_ref()),
448            DataFusionError::IoError(e) => Some(e),
449            #[cfg(feature = "sql")]
450            DataFusionError::SQL(e, _) => Some(e.as_ref()),
451            DataFusionError::NotImplemented(_) => None,
452            DataFusionError::Internal(_) => None,
453            DataFusionError::Configuration(_) => None,
454            DataFusionError::Plan(_) => None,
455            DataFusionError::SchemaError(e, _) => Some(e.as_ref()),
456            DataFusionError::Execution(_) => None,
457            DataFusionError::ExecutionJoin(e) => Some(e.as_ref()),
458            DataFusionError::ResourcesExhausted(_) => None,
459            DataFusionError::External(e) => Some(e.as_ref()),
460            DataFusionError::Context(_, e) => Some(e.as_ref()),
461            DataFusionError::Substrait(_) => None,
462            DataFusionError::Diagnostic(_, e) => Some(e.as_ref()),
463            // Can't really make a Collection fit into the mold of "an error has
464            // at most one source", but returning the first one is probably good
465            // idea. Especially since `DataFusionError::Collection` is mostly
466            // meant for consumption by the end user, so shouldn't interfere
467            // with programmatic usage too much. Plus, having 1 or 5 errors
468            // doesn't really change the fact that the query is invalid and
469            // can't be executed.
470            DataFusionError::Collection(errs) => errs.first().map(|e| e as &dyn Error),
471            DataFusionError::Shared(e) => Some(e.as_ref()),
472            DataFusionError::Ffi(_) => None,
473        }
474    }
475}
476
477impl From<DataFusionError> for io::Error {
478    fn from(e: DataFusionError) -> Self {
479        io::Error::other(e)
480    }
481}
482
483impl DataFusionError {
484    /// The separator between the error message and the backtrace
485    pub const BACK_TRACE_SEP: &'static str = "\n\nbacktrace: ";
486
487    /// Get deepest underlying [`DataFusionError`]
488    ///
489    /// [`DataFusionError`]s sometimes form a chain, such as `DataFusionError::ArrowError()` in order to conform
490    /// to the correct error signature. Thus sometimes there is a chain several layers deep that can obscure the
491    /// original error. This function finds the lowest level DataFusionError possible.
492    ///
493    /// For example,  `find_root` will return`DataFusionError::ResourceExhausted` given the input
494    /// ```text
495    /// DataFusionError::ArrowError
496    ///   ArrowError::External
497    ///    Box(DataFusionError::Context)
498    ///      DataFusionError::ResourceExhausted
499    /// ```
500    ///
501    /// This may be the same as `self`.
502    pub fn find_root(&self) -> &Self {
503        // Note: This is a non-recursive algorithm so we do not run
504        // out of stack space, even for long error chains.
505
506        let mut last_datafusion_error = self;
507        let mut root_error: &dyn Error = self;
508        while let Some(source) = root_error.source() {
509            // walk the next level
510            root_error = source;
511            // remember the lowest datafusion error so far
512            if let Some(e) = root_error.downcast_ref::<DataFusionError>() {
513                last_datafusion_error = e;
514            } else if let Some(e) = root_error.downcast_ref::<Arc<DataFusionError>>() {
515                // As `Arc<T>::source()` calls through to `T::source()` we need to
516                // explicitly match `Arc<DataFusionError>` to capture it
517                last_datafusion_error = e.as_ref();
518            }
519        }
520        // return last checkpoint (which may be the original error)
521        last_datafusion_error
522    }
523
524    /// wraps self in Self::Context with a description
525    pub fn context(self, description: impl Into<String>) -> Self {
526        Self::Context(description.into(), Box::new(self))
527    }
528
529    /// Strips backtrace out of the error message
530    /// If backtrace enabled then error has a format "message" [`Self::BACK_TRACE_SEP`] "backtrace"
531    /// The method strips the backtrace and outputs "message"
532    pub fn strip_backtrace(&self) -> String {
533        (*self
534            .to_string()
535            .split(Self::BACK_TRACE_SEP)
536            .collect::<Vec<&str>>()
537            .first()
538            .unwrap_or(&""))
539        .to_string()
540    }
541
542    /// To enable optional rust backtrace in DataFusion:
543    /// - [`Setup Env Variables`]<https://doc.rust-lang.org/std/backtrace/index.html#environment-variables>
544    /// - Enable `backtrace` cargo feature
545    ///
546    /// Example:
547    /// cargo build --features 'backtrace'
548    /// RUST_BACKTRACE=1 ./app
549    #[inline(always)]
550    pub fn get_back_trace() -> String {
551        #[cfg(feature = "backtrace")]
552        {
553            let back_trace = Backtrace::capture();
554            if back_trace.status() == BacktraceStatus::Captured {
555                return format!("{}{}", Self::BACK_TRACE_SEP, back_trace);
556            }
557
558            "".to_owned()
559        }
560
561        #[cfg(not(feature = "backtrace"))]
562        "".to_owned()
563    }
564
565    /// Return a [`DataFusionErrorBuilder`] to build a [`DataFusionError`]
566    pub fn builder() -> DataFusionErrorBuilder {
567        DataFusionErrorBuilder::default()
568    }
569
570    fn error_prefix(&self) -> &'static str {
571        match self {
572            DataFusionError::ArrowError(_, _) => "Arrow error: ",
573            #[cfg(feature = "parquet")]
574            DataFusionError::ParquetError(_) => "Parquet error: ",
575            #[cfg(feature = "object_store")]
576            DataFusionError::ObjectStore(_) => "Object Store error: ",
577            DataFusionError::IoError(_) => "IO error: ",
578            #[cfg(feature = "sql")]
579            DataFusionError::SQL(_, _) => "SQL error: ",
580            DataFusionError::NotImplemented(_) => {
581                "This feature is not implemented: "
582            }
583            DataFusionError::Internal(_) => "Internal error: ",
584            DataFusionError::Plan(_) => "Error during planning: ",
585            DataFusionError::Configuration(_) => {
586                "Invalid or Unsupported Configuration: "
587            }
588            DataFusionError::SchemaError(_, _) => "Schema error: ",
589            DataFusionError::Execution(_) => "Execution error: ",
590            DataFusionError::ExecutionJoin(_) => "ExecutionJoin error: ",
591            DataFusionError::ResourcesExhausted(_) => {
592                "Resources exhausted: "
593            }
594            DataFusionError::External(_) => "External error: ",
595            DataFusionError::Context(_, _) => "",
596            DataFusionError::Substrait(_) => "Substrait error: ",
597            DataFusionError::Diagnostic(_, _) => "",
598            DataFusionError::Collection(errs) => {
599                errs.first().expect("cannot construct DataFusionError::Collection with 0 errors, but got one such case").error_prefix()
600            }
601            DataFusionError::Shared(_) => "",
602            DataFusionError::Ffi(_) => "FFI error: ",
603        }
604    }
605
606    pub fn message(&self) -> Cow<'_, str> {
607        match *self {
608            DataFusionError::ArrowError(ref desc, ref backtrace) => {
609                let backtrace = backtrace.clone().unwrap_or_else(|| "".to_owned());
610                Cow::Owned(format!("{desc}{backtrace}"))
611            }
612            #[cfg(feature = "parquet")]
613            DataFusionError::ParquetError(ref desc) => Cow::Owned(desc.to_string()),
614            DataFusionError::IoError(ref desc) => Cow::Owned(desc.to_string()),
615            #[cfg(feature = "sql")]
616            DataFusionError::SQL(ref desc, ref backtrace) => {
617                let backtrace: String =
618                    backtrace.clone().unwrap_or_else(|| "".to_owned());
619                Cow::Owned(format!("{desc:?}{backtrace}"))
620            }
621            DataFusionError::Configuration(ref desc) => Cow::Owned(desc.to_string()),
622            DataFusionError::NotImplemented(ref desc) => Cow::Owned(desc.to_string()),
623            DataFusionError::Internal(ref desc) => Cow::Owned(format!(
624                "{desc}.\nThis issue was likely caused by a bug in DataFusion's code. \
625                Please help us to resolve this by filing a bug report in our issue tracker: \
626                https://github.com/apache/datafusion/issues"
627            )),
628            DataFusionError::Plan(ref desc) => Cow::Owned(desc.to_string()),
629            DataFusionError::SchemaError(ref desc, ref backtrace) => {
630                let backtrace: &str =
631                    &backtrace.as_ref().clone().unwrap_or_else(|| "".to_owned());
632                Cow::Owned(format!("{desc}{backtrace}"))
633            }
634            DataFusionError::Execution(ref desc) => Cow::Owned(desc.to_string()),
635            DataFusionError::ExecutionJoin(ref desc) => Cow::Owned(desc.to_string()),
636            DataFusionError::ResourcesExhausted(ref desc) => Cow::Owned(desc.to_string()),
637            DataFusionError::External(ref desc) => Cow::Owned(desc.to_string()),
638            #[cfg(feature = "object_store")]
639            DataFusionError::ObjectStore(ref desc) => Cow::Owned(desc.to_string()),
640            DataFusionError::Context(ref desc, ref err) => {
641                Cow::Owned(format!("{desc}\ncaused by\n{}", *err))
642            }
643            DataFusionError::Substrait(ref desc) => Cow::Owned(desc.to_string()),
644            DataFusionError::Diagnostic(_, ref err) => Cow::Owned(err.to_string()),
645            // Returning the message of the first error is probably fine enough,
646            // and makes `DataFusionError::Collection` a transparent wrapped,
647            // unless the end user explicitly calls `DataFusionError::iter`.
648            DataFusionError::Collection(ref errs) => errs
649                .first()
650                .expect("cannot construct DataFusionError::Collection with 0 errors")
651                .message(),
652            DataFusionError::Shared(ref desc) => Cow::Owned(desc.to_string()),
653            DataFusionError::Ffi(ref desc) => Cow::Owned(desc.to_string()),
654        }
655    }
656
657    /// Wraps the error with contextual information intended for end users
658    pub fn with_diagnostic(self, diagnostic: Diagnostic) -> Self {
659        Self::Diagnostic(Box::new(diagnostic), Box::new(self))
660    }
661
662    /// Wraps the error with contextual information intended for end users.
663    /// Takes a function that inspects the error and returns the diagnostic to
664    /// wrap it with.
665    pub fn with_diagnostic_fn<F: FnOnce(&DataFusionError) -> Diagnostic>(
666        self,
667        f: F,
668    ) -> Self {
669        let diagnostic = f(&self);
670        self.with_diagnostic(diagnostic)
671    }
672
673    /// Gets the [`Diagnostic`] associated with the error, if any. If there is
674    /// more than one, only the outermost [`Diagnostic`] is returned.
675    pub fn diagnostic(&self) -> Option<&Diagnostic> {
676        struct DiagnosticsIterator<'a> {
677            head: &'a DataFusionError,
678        }
679
680        impl<'a> Iterator for DiagnosticsIterator<'a> {
681            type Item = &'a Diagnostic;
682
683            fn next(&mut self) -> Option<Self::Item> {
684                loop {
685                    if let DataFusionError::Diagnostic(diagnostics, source) = self.head {
686                        self.head = source.as_ref();
687                        return Some(diagnostics);
688                    }
689
690                    {
691                        let source = self.head.source().and_then(|source| {
692                            source.downcast_ref::<DataFusionError>()
693                        })?;
694                        self.head = source;
695                    }
696                }
697            }
698        }
699
700        DiagnosticsIterator { head: self }.next()
701    }
702
703    /// Return an iterator over this [`DataFusionError`] and any other
704    /// [`DataFusionError`]s in a [`DataFusionError::Collection`].
705    ///
706    /// Sometimes DataFusion is able to collect multiple errors in a SQL query
707    /// before terminating, e.g. across different expressions in a SELECT
708    /// statements or different sides of a UNION. This method returns an
709    /// iterator over all the errors in the collection.
710    ///
711    /// For this to work, the top-level error must be a
712    /// `DataFusionError::Collection`, not something that contains it.
713    pub fn iter(&self) -> impl Iterator<Item = &DataFusionError> {
714        struct ErrorIterator<'a> {
715            queue: VecDeque<&'a DataFusionError>,
716        }
717
718        impl<'a> Iterator for ErrorIterator<'a> {
719            type Item = &'a DataFusionError;
720
721            fn next(&mut self) -> Option<Self::Item> {
722                loop {
723                    let popped = self.queue.pop_front()?;
724                    match popped {
725                        DataFusionError::Collection(errs) => self.queue.extend(errs),
726                        _ => return Some(popped),
727                    }
728                }
729            }
730        }
731
732        let mut queue = VecDeque::new();
733        queue.push_back(self);
734        ErrorIterator { queue }
735    }
736}
737
738/// A builder for [`DataFusionError`]
739///
740/// This builder can be used to collect multiple errors and return them as a
741/// [`DataFusionError::Collection`].
742///
743/// # Example: no errors
744/// ```
745/// # use datafusion_common::DataFusionError;
746/// let mut builder = DataFusionError::builder();
747/// // ok_or returns the value if no errors have been added
748/// assert_eq!(builder.error_or(42).unwrap(), 42);
749/// ```
750///
751/// # Example: with errors
752/// ```
753/// # use datafusion_common::{assert_contains, DataFusionError};
754/// let mut builder = DataFusionError::builder();
755/// builder.add_error(DataFusionError::Internal("foo".to_owned()));
756/// // ok_or returns the value if no errors have been added
757/// assert_contains!(
758///     builder.error_or(42).unwrap_err().to_string(),
759///     "Internal error: foo"
760/// );
761/// ```
762#[derive(Debug, Default)]
763pub struct DataFusionErrorBuilder(Vec<DataFusionError>);
764
765impl DataFusionErrorBuilder {
766    /// Create a new [`DataFusionErrorBuilder`]
767    pub fn new() -> Self {
768        Default::default()
769    }
770
771    /// Add an error to the in progress list
772    ///
773    /// # Example
774    /// ```
775    /// # use datafusion_common::{assert_contains, DataFusionError};
776    /// let mut builder = DataFusionError::builder();
777    /// builder.add_error(DataFusionError::Internal("foo".to_owned()));
778    /// assert_contains!(
779    ///     builder.error_or(42).unwrap_err().to_string(),
780    ///     "Internal error: foo"
781    /// );
782    /// ```
783    pub fn add_error(&mut self, error: DataFusionError) {
784        self.0.push(error);
785    }
786
787    /// Add an error to the in progress list, returning the builder
788    ///
789    /// # Example
790    /// ```
791    /// # use datafusion_common::{assert_contains, DataFusionError};
792    /// let builder = DataFusionError::builder()
793    ///     .with_error(DataFusionError::Internal("foo".to_owned()));
794    /// assert_contains!(
795    ///     builder.error_or(42).unwrap_err().to_string(),
796    ///     "Internal error: foo"
797    /// );
798    /// ```
799    pub fn with_error(mut self, error: DataFusionError) -> Self {
800        self.0.push(error);
801        self
802    }
803
804    /// Returns `Ok(ok)` if no errors were added to the builder,
805    /// otherwise returns a `Result::Err`
806    pub fn error_or<T>(self, ok: T) -> Result<T, DataFusionError> {
807        match self.0.len() {
808            0 => Ok(ok),
809            1 => Err(self.0.into_iter().next().expect("length matched 1")),
810            _ => Err(DataFusionError::Collection(self.0)),
811        }
812    }
813}
814
815/// Unwrap an `Option` if possible. Otherwise return an `DataFusionError::Internal`.
816/// In normal usage of DataFusion the unwrap should always succeed.
817///
818/// Example: `let values = unwrap_or_internal_err!(values)`
819#[macro_export]
820macro_rules! unwrap_or_internal_err {
821    ($Value: ident) => {
822        $Value.ok_or_else(|| {
823            $crate::error::_internal_datafusion_err!(
824                "{} should not be None",
825                stringify!($Value)
826            )
827        })?
828    };
829}
830
831/// Assert a condition, returning `DataFusionError::Internal` on failure.
832///
833/// # Examples
834///
835/// ```text
836/// assert_or_internal_err!(predicate);
837/// assert_or_internal_err!(predicate, "human readable message");
838/// assert_or_internal_err!(predicate, format!("details: {}", value));
839/// ```
840#[macro_export]
841macro_rules! assert_or_internal_err {
842    ($cond:expr) => {
843        if !$cond {
844            return Err($crate::error::_internal_datafusion_err!(
845                "Assertion failed: {}",
846                stringify!($cond)
847            ));
848        }
849    };
850    ($cond:expr, $($arg:tt)+) => {
851        if !$cond {
852            return Err($crate::error::_internal_datafusion_err!(
853                "Assertion failed: {}: {}",
854                stringify!($cond),
855                format!($($arg)+)
856            ));
857        }
858    };
859}
860
861/// Assert equality, returning `DataFusionError::Internal` on failure.
862///
863/// # Examples
864///
865/// ```text
866/// assert_eq_or_internal_err!(actual, expected);
867/// assert_eq_or_internal_err!(left_expr, right_expr, "values must match");
868/// assert_eq_or_internal_err!(lhs, rhs, "metadata: {}", extra);
869/// ```
870#[macro_export]
871macro_rules! assert_eq_or_internal_err {
872    ($left:expr, $right:expr $(,)?) => {{
873        let left_val = &$left;
874        let right_val = &$right;
875        if left_val != right_val {
876            return Err($crate::error::_internal_datafusion_err!(
877                "Assertion failed: {} == {} (left: {:?}, right: {:?})",
878                stringify!($left),
879                stringify!($right),
880                left_val,
881                right_val
882            ));
883        }
884    }};
885    ($left:expr, $right:expr, $($arg:tt)+) => {{
886        let left_val = &$left;
887        let right_val = &$right;
888        if left_val != right_val {
889            return Err($crate::error::_internal_datafusion_err!(
890                "Assertion failed: {} == {} (left: {:?}, right: {:?}): {}",
891                stringify!($left),
892                stringify!($right),
893                left_val,
894                right_val,
895                format!($($arg)+)
896            ));
897        }
898    }};
899}
900
901/// Assert inequality, returning `DataFusionError::Internal` on failure.
902///
903/// # Examples
904///
905/// ```text
906/// assert_ne_or_internal_err!(left, right);
907/// assert_ne_or_internal_err!(lhs_expr, rhs_expr, "values must differ");
908/// assert_ne_or_internal_err!(a, b, "context {}", info);
909/// ```
910#[macro_export]
911macro_rules! assert_ne_or_internal_err {
912    ($left:expr, $right:expr $(,)?) => {{
913        let left_val = &$left;
914        let right_val = &$right;
915        if left_val == right_val {
916            return Err($crate::error::_internal_datafusion_err!(
917                "Assertion failed: {} != {} (left: {:?}, right: {:?})",
918                stringify!($left),
919                stringify!($right),
920                left_val,
921                right_val
922            ));
923        }
924    }};
925    ($left:expr, $right:expr, $($arg:tt)+) => {{
926        let left_val = &$left;
927        let right_val = &$right;
928        if left_val == right_val {
929            return Err($crate::error::_internal_datafusion_err!(
930                "Assertion failed: {} != {} (left: {:?}, right: {:?}): {}",
931                stringify!($left),
932                stringify!($right),
933                left_val,
934                right_val,
935                format!($($arg)+)
936            ));
937        }
938    }};
939}
940
941/// Add a macros for concise  DataFusionError::* errors declaration
942/// supports placeholders the same way as `format!`
943/// Examples:
944///     plan_err!("Error")
945///     plan_err!("Error {}", val)
946///     plan_err!("Error {:?}", val)
947///     plan_err!("Error {val}")
948///     plan_err!("Error {val:?}")
949///
950/// `NAME_ERR` -  macro name for wrapping Err(DataFusionError::*)
951/// `PREFIXED_NAME_ERR` - underscore-prefixed alias for NAME_ERR (e.g., _plan_err)
952/// (Needed to avoid compiler error when using macro in the same crate: `macros from the current crate cannot be referred to by absolute paths`)
953/// `NAME_DF_ERR` -  macro name for wrapping DataFusionError::*. Needed to keep backtrace opportunity
954/// in construction where DataFusionError::* used directly, like `map_err`, `ok_or_else`, etc
955/// `PREFIXED_NAME_DF_ERR` - underscore-prefixed alias for NAME_DF_ERR (e.g., _plan_datafusion_err).
956/// (Needed to avoid compiler error when using macro in the same crate: `macros from the current crate cannot be referred to by absolute paths`)
957macro_rules! make_error {
958    ($NAME_ERR:ident, $PREFIXED_NAME_ERR:ident, $NAME_DF_ERR:ident, $PREFIXED_NAME_DF_ERR:ident, $ERR:ident) => {
959        make_error!(@inner ($), $NAME_ERR, $PREFIXED_NAME_ERR, $NAME_DF_ERR, $PREFIXED_NAME_DF_ERR, $ERR);
960    };
961    (@inner ($d:tt), $NAME_ERR:ident, $PREFIXED_NAME_ERR:ident, $NAME_DF_ERR:ident, $PREFIXED_NAME_DF_ERR:ident, $ERR:ident) => {
962        /// Macro wraps `$ERR` to add backtrace feature
963        #[macro_export]
964        macro_rules! $NAME_DF_ERR {
965            ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
966                let err = $crate::DataFusionError::$ERR(
967                    ::std::format!(
968                        "{}{}",
969                        ::std::format!($d($d args),*),
970                        $crate::DataFusionError::get_back_trace(),
971                    ).into()
972                );
973                $d (
974                    let err = err.with_diagnostic($d DIAG);
975                )?
976                err
977            }}
978        }
979
980        /// Macro wraps Err(`$ERR`) to add backtrace feature
981        #[macro_export]
982        macro_rules! $NAME_ERR {
983            ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
984                let err = $crate::$PREFIXED_NAME_DF_ERR!($d($d args),*);
985                $d (
986                    let err = err.with_diagnostic($d DIAG);
987                )?
988                Err(err)
989            }}
990        }
991
992        #[doc(hidden)]
993        pub use $NAME_ERR as $PREFIXED_NAME_ERR;
994        #[doc(hidden)]
995        pub use $NAME_DF_ERR as $PREFIXED_NAME_DF_ERR;
996    };
997}
998
999// Exposes a macro to create `DataFusionError::Plan` with optional backtrace
1000make_error!(
1001    plan_err,
1002    _plan_err,
1003    plan_datafusion_err,
1004    _plan_datafusion_err,
1005    Plan
1006);
1007
1008// Exposes a macro to create `DataFusionError::Internal` with optional backtrace
1009make_error!(
1010    internal_err,
1011    _internal_err,
1012    internal_datafusion_err,
1013    _internal_datafusion_err,
1014    Internal
1015);
1016
1017// Exposes a macro to create `DataFusionError::NotImplemented` with optional backtrace
1018make_error!(
1019    not_impl_err,
1020    _not_impl_err,
1021    not_impl_datafusion_err,
1022    _not_impl_datafusion_err,
1023    NotImplemented
1024);
1025
1026// Exposes a macro to create `DataFusionError::Execution` with optional backtrace
1027make_error!(
1028    exec_err,
1029    _exec_err,
1030    exec_datafusion_err,
1031    _exec_datafusion_err,
1032    Execution
1033);
1034
1035// Exposes a macro to create `DataFusionError::Configuration` with optional backtrace
1036make_error!(
1037    config_err,
1038    _config_err,
1039    config_datafusion_err,
1040    _config_datafusion_err,
1041    Configuration
1042);
1043
1044// Exposes a macro to create `DataFusionError::Substrait` with optional backtrace
1045make_error!(
1046    substrait_err,
1047    _substrait_err,
1048    substrait_datafusion_err,
1049    _substrait_datafusion_err,
1050    Substrait
1051);
1052
1053// Exposes a macro to create `DataFusionError::ResourcesExhausted` with optional backtrace
1054make_error!(
1055    resources_err,
1056    _resources_err,
1057    resources_datafusion_err,
1058    _resources_datafusion_err,
1059    ResourcesExhausted
1060);
1061
1062// Exposes a macro to create `DataFusionError::Ffi` with optional backtrace
1063make_error!(
1064    ffi_err,
1065    _ffi_err,
1066    ffi_datafusion_err,
1067    _ffi_datafusion_err,
1068    Ffi
1069);
1070
1071// Exposes a macro to create `DataFusionError::SQL` with optional backtrace
1072#[macro_export]
1073macro_rules! sql_datafusion_err {
1074    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1075        let err = $crate::DataFusionError::SQL(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1076        $(
1077            let err = err.with_diagnostic($DIAG);
1078        )?
1079        err
1080    }};
1081}
1082
1083// Exposes a macro to create `Err(DataFusionError::SQL)` with optional backtrace
1084#[macro_export]
1085macro_rules! sql_err {
1086    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1087        let err = $crate::sql_datafusion_err!($ERR);
1088        $(
1089            let err = err.with_diagnostic($DIAG);
1090        )?
1091        Err(err)
1092    }};
1093}
1094
1095// Exposes a macro to create `DataFusionError::ArrowError` with optional backtrace
1096#[macro_export]
1097macro_rules! arrow_datafusion_err {
1098    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1099        let err = $crate::DataFusionError::ArrowError(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1100        $(
1101            let err = err.with_diagnostic($DIAG);
1102        )?
1103        err
1104    }};
1105}
1106
1107// Exposes a macro to create `Err(DataFusionError::ArrowError)` with optional backtrace
1108#[macro_export]
1109macro_rules! arrow_err {
1110    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {
1111    {
1112        let err = $crate::arrow_datafusion_err!($ERR);
1113        $(
1114            let err = err.with_diagnostic($DIAG);
1115        )?
1116        Err(err)
1117    }};
1118}
1119
1120// Exposes a macro to create `DataFusionError::SchemaError` with optional backtrace
1121#[macro_export]
1122macro_rules! schema_datafusion_err {
1123    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1124        let err = $crate::DataFusionError::SchemaError(
1125            Box::new($ERR),
1126            Box::new(Some($crate::DataFusionError::get_back_trace())),
1127        );
1128        $(
1129            let err = err.with_diagnostic($DIAG);
1130        )?
1131        err
1132    }};
1133}
1134
1135// Exposes a macro to create `Err(DataFusionError::SchemaError)` with optional backtrace
1136#[macro_export]
1137macro_rules! schema_err {
1138    ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1139        let err = $crate::DataFusionError::SchemaError(
1140            Box::new($ERR),
1141            Box::new(Some($crate::DataFusionError::get_back_trace())),
1142        );
1143        $(
1144            let err = err.with_diagnostic($DIAG);
1145        )?
1146        Err(err)
1147    }
1148    };
1149}
1150
1151// To avoid compiler error when using macro in the same crate:
1152// macros from the current crate cannot be referred to by absolute paths
1153pub use schema_err as _schema_err;
1154
1155/// Create a "field not found" DataFusion::SchemaError
1156pub fn field_not_found<R: Into<TableReference>>(
1157    qualifier: Option<R>,
1158    name: &str,
1159    schema: &DFSchema,
1160) -> DataFusionError {
1161    schema_datafusion_err!(SchemaError::FieldNotFound {
1162        field: Box::new(Column::new(qualifier, name)),
1163        valid_fields: schema.columns().to_vec(),
1164    })
1165}
1166
1167/// Convenience wrapper over [`field_not_found`] for when there is no qualifier
1168pub fn unqualified_field_not_found(name: &str, schema: &DFSchema) -> DataFusionError {
1169    schema_datafusion_err!(SchemaError::FieldNotFound {
1170        field: Box::new(Column::new_unqualified(name)),
1171        valid_fields: schema.columns().to_vec(),
1172    })
1173}
1174
1175pub fn add_possible_columns_to_diag(
1176    diagnostic: &mut Diagnostic,
1177    field: &Column,
1178    valid_fields: &[Column],
1179) {
1180    let field_names: Vec<String> = valid_fields
1181        .iter()
1182        .filter_map(|f| {
1183            if normalized_levenshtein(f.name(), field.name()) >= 0.5 {
1184                Some(f.flat_name())
1185            } else {
1186                None
1187            }
1188        })
1189        .collect();
1190
1191    for name in field_names {
1192        diagnostic.add_note(format!("possible column {name}"), None);
1193    }
1194}
1195
1196#[cfg(test)]
1197mod test {
1198    use super::*;
1199
1200    use std::mem::size_of;
1201    use std::sync::Arc;
1202
1203    use arrow::error::ArrowError;
1204
1205    fn ok_result() -> Result<()> {
1206        Ok(())
1207    }
1208
1209    #[test]
1210    fn test_assert_eq_or_internal_err_passes() -> Result<()> {
1211        assert_eq_or_internal_err!(1, 1);
1212        ok_result()
1213    }
1214
1215    #[test]
1216    fn test_assert_eq_or_internal_err_fails() {
1217        fn check() -> Result<()> {
1218            assert_eq_or_internal_err!(1, 2, "expected equality");
1219            ok_result()
1220        }
1221
1222        let err = check().unwrap_err().strip_backtrace();
1223        assert!(err.starts_with("Internal error: Assertion failed: 1 == 2 (left: 1, right: 2): expected equality"));
1224    }
1225
1226    #[test]
1227    fn test_assert_ne_or_internal_err_passes() -> Result<()> {
1228        assert_ne_or_internal_err!(1, 2);
1229        ok_result()
1230    }
1231
1232    #[test]
1233    fn test_assert_ne_or_internal_err_fails() {
1234        fn check() -> Result<()> {
1235            assert_ne_or_internal_err!(3, 3, "values must differ");
1236            ok_result()
1237        }
1238
1239        let err = check().unwrap_err().strip_backtrace();
1240        assert!(err.starts_with("Internal error: Assertion failed: 3 != 3 (left: 3, right: 3): values must differ"));
1241    }
1242
1243    #[test]
1244    fn test_assert_or_internal_err_passes() -> Result<()> {
1245        assert_or_internal_err!(true);
1246        assert_or_internal_err!(true, "message");
1247        ok_result()
1248    }
1249
1250    #[test]
1251    fn test_assert_or_internal_err_fails_default() {
1252        fn check() -> Result<()> {
1253            assert_or_internal_err!(false);
1254            ok_result()
1255        }
1256
1257        let err = check().unwrap_err().strip_backtrace();
1258        assert!(err.starts_with("Internal error: Assertion failed: false"));
1259    }
1260
1261    #[test]
1262    fn test_assert_or_internal_err_fails_with_message() {
1263        fn check() -> Result<()> {
1264            assert_or_internal_err!(false, "custom message");
1265            ok_result()
1266        }
1267
1268        let err = check().unwrap_err().strip_backtrace();
1269        assert!(
1270            err.starts_with("Internal error: Assertion failed: false: custom message")
1271        );
1272    }
1273
1274    #[test]
1275    fn test_assert_or_internal_err_with_format_arguments() {
1276        fn check() -> Result<()> {
1277            assert_or_internal_err!(false, "custom {}", 42);
1278            ok_result()
1279        }
1280
1281        let err = check().unwrap_err().strip_backtrace();
1282        assert!(err.starts_with("Internal error: Assertion failed: false: custom 42"));
1283    }
1284
1285    #[test]
1286    fn test_error_size() {
1287        // Since Errors influence the size of Result which influence the size of the stack
1288        // please don't allow this to grow larger
1289        assert_eq!(size_of::<SchemaError>(), 40);
1290        assert_eq!(size_of::<DataFusionError>(), 40);
1291    }
1292
1293    #[test]
1294    fn datafusion_error_to_arrow() {
1295        let res = return_arrow_error().unwrap_err();
1296        assert!(
1297            res.to_string()
1298                .starts_with("External error: Error during planning: foo")
1299        );
1300    }
1301
1302    #[test]
1303    fn arrow_error_to_datafusion() {
1304        let res = return_datafusion_error().unwrap_err();
1305        assert_eq!(res.strip_backtrace(), "Arrow error: Schema error: bar");
1306    }
1307
1308    // To pass the test the environment variable RUST_BACKTRACE should be set to 1 to enforce backtrace
1309    #[cfg(feature = "backtrace")]
1310    fn ensure_rust_backtrace_enabled() {
1311        match std::env::var("RUST_BACKTRACE") {
1312            Ok(val) if val == "1" => {}
1313            _ => panic!("Environment variable RUST_BACKTRACE must be set to 1"),
1314        };
1315    }
1316
1317    // To pass the test the environment variable RUST_BACKTRACE should be set to 1 to enforce backtrace
1318    #[cfg(feature = "backtrace")]
1319    #[test]
1320    fn test_enabled_backtrace() {
1321        ensure_rust_backtrace_enabled();
1322
1323        let res: Result<(), DataFusionError> = plan_err!("Err");
1324        assert_error_have_message_and_backtrace(
1325            &res.unwrap_err(),
1326            "Error during planning: Err",
1327        );
1328    }
1329
1330    #[cfg(not(feature = "backtrace"))]
1331    #[test]
1332    fn test_disabled_backtrace() {
1333        let res: Result<(), DataFusionError> = plan_err!("Err");
1334        assert_err_without_backtrace_and_equal(
1335            &res.unwrap_err(),
1336            "Error during planning: Err",
1337        );
1338    }
1339
1340    #[cfg(not(feature = "backtrace"))]
1341    fn assert_err_without_backtrace_and_equal(
1342        err: &DataFusionError,
1343        expected_message: &str,
1344    ) {
1345        let err = err.to_string();
1346        assert!(!err.contains(DataFusionError::BACK_TRACE_SEP));
1347        assert_eq!(err, expected_message);
1348    }
1349
1350    #[cfg(not(feature = "backtrace"))]
1351    fn assert_internal_err_without_backtrace_and_equal(
1352        err: &DataFusionError,
1353        expected_message: &str,
1354    ) {
1355        let expected_message_before_backtrace = format!(
1356            "{expected_message}.\nThis issue was likely caused by a bug in DataFusion's code. \
1357                    Please help us to resolve this by filing a bug report in our issue tracker: \
1358                    https://github.com/apache/datafusion/issues"
1359        );
1360        assert_err_without_backtrace_and_equal(
1361            err,
1362            expected_message_before_backtrace.as_str(),
1363        );
1364    }
1365
1366    #[cfg(feature = "backtrace")]
1367    fn assert_error_have_message_and_backtrace(
1368        err: &DataFusionError,
1369        message_before_backtrace: &str,
1370    ) {
1371        let err = err.to_string();
1372        assert!(err.contains(DataFusionError::BACK_TRACE_SEP));
1373        assert!(
1374            !err.split(DataFusionError::BACK_TRACE_SEP)
1375                .collect::<Vec<&str>>()
1376                .get(1)
1377                .unwrap()
1378                .is_empty()
1379        );
1380        assert_eq!(
1381            err.split(DataFusionError::BACK_TRACE_SEP)
1382                .collect::<Vec<&str>>()
1383                .first()
1384                .copied()
1385                .unwrap(),
1386            message_before_backtrace,
1387            "full error is: {err}"
1388        );
1389    }
1390
1391    #[cfg(feature = "backtrace")]
1392    #[test]
1393    fn test_enabled_backtrace_for_unwrap_or_internal_err() {
1394        ensure_rust_backtrace_enabled();
1395
1396        fn get_error() -> Result<(), DataFusionError> {
1397            let item = None::<()>;
1398            unwrap_or_internal_err!(item);
1399
1400            unreachable!("should return error");
1401        }
1402
1403        let res: Result<(), DataFusionError> = get_error();
1404        assert_error_have_message_and_backtrace(
1405            &res.unwrap_err(),
1406            "Internal error: item should not be None",
1407        );
1408    }
1409
1410    // To pass the test the environment variable RUST_BACKTRACE should be set to 1 to enforce backtrace
1411    #[cfg(not(feature = "backtrace"))]
1412    #[test]
1413    fn test_disabled_backtrace_for_unwrap_or_internal_err() {
1414        fn get_error() -> Result<(), DataFusionError> {
1415            let item = None::<()>;
1416            unwrap_or_internal_err!(item);
1417
1418            unreachable!("should return error");
1419        }
1420
1421        let res: Result<(), DataFusionError> = get_error();
1422        assert_internal_err_without_backtrace_and_equal(
1423            &res.unwrap_err(),
1424            "Internal error: item should not be None",
1425        );
1426    }
1427
1428    #[cfg(feature = "backtrace")]
1429    #[test]
1430    fn test_enabled_backtrace_for_assert_or_internal_err_without_args() {
1431        ensure_rust_backtrace_enabled();
1432
1433        fn get_error() -> Result<(), DataFusionError> {
1434            assert_or_internal_err!(false);
1435
1436            unreachable!("should return error");
1437        }
1438
1439        let res: Result<(), DataFusionError> = get_error();
1440        assert_error_have_message_and_backtrace(
1441            &res.unwrap_err(),
1442            "Internal error: Assertion failed: false",
1443        );
1444    }
1445
1446    #[cfg(feature = "backtrace")]
1447    #[test]
1448    fn test_enabled_backtrace_for_assert_or_internal_err_with_args() {
1449        ensure_rust_backtrace_enabled();
1450
1451        fn get_error() -> Result<(), DataFusionError> {
1452            assert_or_internal_err!(false, "my cool context");
1453
1454            unreachable!("should return error");
1455        }
1456
1457        let res: Result<(), DataFusionError> = get_error();
1458        assert_error_have_message_and_backtrace(
1459            &res.unwrap_err(),
1460            "Internal error: Assertion failed: false: my cool context",
1461        );
1462    }
1463
1464    #[cfg(not(feature = "backtrace"))]
1465    #[test]
1466    fn test_disabled_backtrace_for_assert_or_internal_err_without_args() {
1467        fn get_error() -> Result<(), DataFusionError> {
1468            assert_or_internal_err!(false);
1469
1470            unreachable!("should return error");
1471        }
1472
1473        let res: Result<(), DataFusionError> = get_error();
1474        assert_internal_err_without_backtrace_and_equal(
1475            &res.unwrap_err(),
1476            "Internal error: Assertion failed: false",
1477        );
1478    }
1479
1480    #[cfg(not(feature = "backtrace"))]
1481    #[test]
1482    fn test_disabled_backtrace_for_assert_or_internal_err_with_args() {
1483        fn get_error() -> Result<(), DataFusionError> {
1484            assert_or_internal_err!(false, "my cool context");
1485
1486            unreachable!("should return error");
1487        }
1488
1489        let res: Result<(), DataFusionError> = get_error();
1490        assert_internal_err_without_backtrace_and_equal(
1491            &res.unwrap_err(),
1492            "Internal error: Assertion failed: false: my cool context",
1493        );
1494    }
1495
1496    #[cfg(feature = "backtrace")]
1497    #[test]
1498    fn test_enabled_backtrace_for_assert_eq_or_internal_err_without_args() {
1499        ensure_rust_backtrace_enabled();
1500
1501        fn get_error() -> Result<(), DataFusionError> {
1502            let arg1 = 1;
1503            let arg2 = 2;
1504            assert_eq_or_internal_err!(arg1, arg2);
1505
1506            unreachable!("should return error");
1507        }
1508
1509        let res: Result<(), DataFusionError> = get_error();
1510        assert_error_have_message_and_backtrace(
1511            &res.unwrap_err(),
1512            "Internal error: Assertion failed: arg1 == arg2 (left: 1, right: 2)",
1513        );
1514    }
1515
1516    #[cfg(feature = "backtrace")]
1517    #[test]
1518    fn test_enabled_backtrace_for_assert_eq_or_internal_err_with_args() {
1519        ensure_rust_backtrace_enabled();
1520
1521        fn get_error() -> Result<(), DataFusionError> {
1522            let arg1 = 1;
1523            let arg2 = 2;
1524            assert_eq_or_internal_err!(arg1, arg2, "my cool context");
1525
1526            unreachable!("should return error");
1527        }
1528
1529        let res: Result<(), DataFusionError> = get_error();
1530        assert_error_have_message_and_backtrace(
1531            &res.unwrap_err(),
1532            "Internal error: Assertion failed: arg1 == arg2 (left: 1, right: 2): my cool context",
1533        );
1534    }
1535
1536    #[cfg(not(feature = "backtrace"))]
1537    #[test]
1538    fn test_disabled_backtrace_for_assert_eq_or_internal_err_without_args() {
1539        fn get_error() -> Result<(), DataFusionError> {
1540            let arg1 = 1;
1541            let arg2 = 2;
1542            assert_eq_or_internal_err!(arg1, arg2);
1543
1544            unreachable!("should return error");
1545        }
1546
1547        let res: Result<(), DataFusionError> = get_error();
1548        assert_internal_err_without_backtrace_and_equal(
1549            &res.unwrap_err(),
1550            "Internal error: Assertion failed: arg1 == arg2 (left: 1, right: 2)",
1551        );
1552    }
1553
1554    #[cfg(not(feature = "backtrace"))]
1555    #[test]
1556    fn test_disabled_backtrace_for_assert_eq_or_internal_err_with_args() {
1557        fn get_error() -> Result<(), DataFusionError> {
1558            let arg1 = 1;
1559            let arg2 = 2;
1560            assert_eq_or_internal_err!(arg1, arg2, "my cool context");
1561
1562            unreachable!("should return error");
1563        }
1564
1565        let res: Result<(), DataFusionError> = get_error();
1566        assert_internal_err_without_backtrace_and_equal(
1567            &res.unwrap_err(),
1568            "Internal error: Assertion failed: arg1 == arg2 (left: 1, right: 2): my cool context",
1569        );
1570    }
1571
1572    #[cfg(feature = "backtrace")]
1573    #[test]
1574    fn test_enabled_backtrace_for_assert_ne_or_internal_err_without_args() {
1575        ensure_rust_backtrace_enabled();
1576
1577        fn get_error() -> Result<(), DataFusionError> {
1578            let arg1 = 1;
1579            let arg2 = 1;
1580            assert_ne_or_internal_err!(arg1, arg2);
1581
1582            unreachable!("should return error");
1583        }
1584
1585        let res: Result<(), DataFusionError> = get_error();
1586        assert_error_have_message_and_backtrace(
1587            &res.unwrap_err(),
1588            "Internal error: Assertion failed: arg1 != arg2 (left: 1, right: 1)",
1589        );
1590    }
1591
1592    #[cfg(feature = "backtrace")]
1593    #[test]
1594    fn test_enabled_backtrace_for_assert_ne_or_internal_err_with_args() {
1595        ensure_rust_backtrace_enabled();
1596
1597        fn get_error() -> Result<(), DataFusionError> {
1598            let arg1 = 1;
1599            let arg2 = 1;
1600            assert_ne_or_internal_err!(arg1, arg2, "my cool context");
1601
1602            unreachable!("should return error");
1603        }
1604
1605        let res: Result<(), DataFusionError> = get_error();
1606        assert_error_have_message_and_backtrace(
1607            &res.unwrap_err(),
1608            "Internal error: Assertion failed: arg1 != arg2 (left: 1, right: 1): my cool context",
1609        );
1610    }
1611
1612    #[cfg(not(feature = "backtrace"))]
1613    #[test]
1614    fn test_disabled_backtrace_for_assert_ne_or_internal_err_without_args() {
1615        fn get_error() -> Result<(), DataFusionError> {
1616            let arg1 = 1;
1617            let arg2 = 1;
1618            assert_ne_or_internal_err!(arg1, arg2);
1619
1620            unreachable!("should return error");
1621        }
1622
1623        let res: Result<(), DataFusionError> = get_error();
1624        assert_internal_err_without_backtrace_and_equal(
1625            &res.unwrap_err(),
1626            "Internal error: Assertion failed: arg1 != arg2 (left: 1, right: 1)",
1627        );
1628    }
1629
1630    #[cfg(not(feature = "backtrace"))]
1631    #[test]
1632    fn test_disabled_backtrace_for_assert_ne_or_internal_err_with_args() {
1633        fn get_error() -> Result<(), DataFusionError> {
1634            let arg1 = 1;
1635            let arg2 = 1;
1636            assert_ne_or_internal_err!(arg1, arg2, "my cool context");
1637
1638            unreachable!("should return error");
1639        }
1640
1641        let res: Result<(), DataFusionError> = get_error();
1642        assert_internal_err_without_backtrace_and_equal(
1643            &res.unwrap_err(),
1644            "Internal error: Assertion failed: arg1 != arg2 (left: 1, right: 1): my cool context",
1645        );
1646    }
1647
1648    #[test]
1649    fn test_find_root_error() {
1650        do_root_test(
1651            DataFusionError::Context(
1652                "it happened!".to_string(),
1653                Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1654            ),
1655            DataFusionError::ResourcesExhausted("foo".to_string()),
1656        );
1657
1658        do_root_test(
1659            DataFusionError::ArrowError(
1660                Box::new(ArrowError::ExternalError(Box::new(
1661                    DataFusionError::ResourcesExhausted("foo".to_string()),
1662                ))),
1663                None,
1664            ),
1665            DataFusionError::ResourcesExhausted("foo".to_string()),
1666        );
1667
1668        do_root_test(
1669            DataFusionError::External(Box::new(DataFusionError::ResourcesExhausted(
1670                "foo".to_string(),
1671            ))),
1672            DataFusionError::ResourcesExhausted("foo".to_string()),
1673        );
1674
1675        do_root_test(
1676            DataFusionError::External(Box::new(ArrowError::ExternalError(Box::new(
1677                DataFusionError::ResourcesExhausted("foo".to_string()),
1678            )))),
1679            DataFusionError::ResourcesExhausted("foo".to_string()),
1680        );
1681
1682        do_root_test(
1683            DataFusionError::ArrowError(
1684                Box::new(ArrowError::ExternalError(Box::new(
1685                    ArrowError::ExternalError(Box::new(
1686                        DataFusionError::ResourcesExhausted("foo".to_string()),
1687                    )),
1688                ))),
1689                None,
1690            ),
1691            DataFusionError::ResourcesExhausted("foo".to_string()),
1692        );
1693
1694        do_root_test(
1695            DataFusionError::External(Box::new(Arc::new(
1696                DataFusionError::ResourcesExhausted("foo".to_string()),
1697            ))),
1698            DataFusionError::ResourcesExhausted("foo".to_string()),
1699        );
1700
1701        do_root_test(
1702            DataFusionError::External(Box::new(Arc::new(ArrowError::ExternalError(
1703                Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1704            )))),
1705            DataFusionError::ResourcesExhausted("foo".to_string()),
1706        );
1707    }
1708
1709    #[test]
1710    fn test_make_error_parse_input() {
1711        let res: Result<(), DataFusionError> = plan_err!("Err");
1712        let res = res.unwrap_err();
1713        assert_eq!(res.strip_backtrace(), "Error during planning: Err");
1714
1715        let extra1 = "extra1";
1716        let extra2 = "extra2";
1717
1718        let res: Result<(), DataFusionError> = plan_err!("Err {} {}", extra1, extra2);
1719        let res = res.unwrap_err();
1720        assert_eq!(
1721            res.strip_backtrace(),
1722            "Error during planning: Err extra1 extra2"
1723        );
1724
1725        let res: Result<(), DataFusionError> =
1726            plan_err!("Err {:?} {:#?}", extra1, extra2);
1727        let res = res.unwrap_err();
1728        assert_eq!(
1729            res.strip_backtrace(),
1730            "Error during planning: Err \"extra1\" \"extra2\""
1731        );
1732
1733        let res: Result<(), DataFusionError> = plan_err!("Err {extra1} {extra2}");
1734        let res = res.unwrap_err();
1735        assert_eq!(
1736            res.strip_backtrace(),
1737            "Error during planning: Err extra1 extra2"
1738        );
1739
1740        let res: Result<(), DataFusionError> = plan_err!("Err {extra1:?} {extra2:#?}");
1741        let res = res.unwrap_err();
1742        assert_eq!(
1743            res.strip_backtrace(),
1744            "Error during planning: Err \"extra1\" \"extra2\""
1745        );
1746    }
1747
1748    #[test]
1749    fn external_error() {
1750        // assert not wrapping DataFusionError
1751        let generic_error: GenericError =
1752            Box::new(DataFusionError::Plan("test".to_string()));
1753        let datafusion_error: DataFusionError = generic_error.into();
1754        println!("{}", datafusion_error.strip_backtrace());
1755        assert_eq!(
1756            datafusion_error.strip_backtrace(),
1757            "Error during planning: test"
1758        );
1759
1760        // assert wrapping other Error
1761        let generic_error: GenericError = Box::new(io::Error::other("io error"));
1762        let datafusion_error: DataFusionError = generic_error.into();
1763        println!("{}", datafusion_error.strip_backtrace());
1764        assert_eq!(
1765            datafusion_error.strip_backtrace(),
1766            "External error: io error"
1767        );
1768    }
1769
1770    #[test]
1771    fn external_error_no_recursive() {
1772        let generic_error_1: GenericError = Box::new(io::Error::other("io error"));
1773        let external_error_1: DataFusionError = generic_error_1.into();
1774        let generic_error_2: GenericError = Box::new(external_error_1);
1775        let external_error_2: DataFusionError = generic_error_2.into();
1776
1777        println!("{external_error_2}");
1778        assert!(
1779            external_error_2
1780                .to_string()
1781                .starts_with("External error: io error")
1782        );
1783    }
1784
1785    /// Model what happens when implementing SendableRecordBatchStream:
1786    /// DataFusion code needs to return an ArrowError
1787    fn return_arrow_error() -> arrow::error::Result<()> {
1788        // Expect the '?' to work
1789        Err(DataFusionError::Plan("foo".to_string()).into())
1790    }
1791
1792    /// Model what happens when using arrow kernels in DataFusion
1793    /// code: need to turn an ArrowError into a DataFusionError
1794    fn return_datafusion_error() -> Result<()> {
1795        // Expect the '?' to work
1796        Err(ArrowError::SchemaError("bar".to_string()).into())
1797    }
1798
1799    fn do_root_test(e: DataFusionError, exp: DataFusionError) {
1800        let e = e.find_root();
1801
1802        // DataFusionError does not implement Eq, so we use a string comparison + some cheap "same variant" test instead
1803        assert_eq!(e.strip_backtrace(), exp.strip_backtrace());
1804        assert_eq!(std::mem::discriminant(e), std::mem::discriminant(&exp),)
1805    }
1806
1807    #[test]
1808    fn test_iter() {
1809        let err = DataFusionError::Collection(vec![
1810            DataFusionError::Plan("a".to_string()),
1811            DataFusionError::Collection(vec![
1812                DataFusionError::Plan("b".to_string()),
1813                DataFusionError::Plan("c".to_string()),
1814            ]),
1815        ]);
1816        let errs = err.iter().collect::<Vec<_>>();
1817        assert_eq!(errs.len(), 3);
1818        assert_eq!(errs[0].strip_backtrace(), "Error during planning: a");
1819        assert_eq!(errs[1].strip_backtrace(), "Error during planning: b");
1820        assert_eq!(errs[2].strip_backtrace(), "Error during planning: c");
1821    }
1822}