1#[cfg(feature = "backtrace")]
38use std::backtrace::{Backtrace, BacktraceStatus};
39
40use std::borrow::Cow;
41use std::collections::VecDeque;
42use std::error::Error;
43use std::fmt::{Display, Formatter};
44use std::io;
45use std::result;
46use std::sync::Arc;
47
48use crate::utils::datafusion_strsim::normalized_levenshtein;
49use crate::utils::quote_identifier;
50use crate::{Column, DFSchema, Diagnostic, TableReference};
51#[cfg(feature = "avro")]
52use apache_avro::Error as AvroError;
53use arrow::error::ArrowError;
54#[cfg(feature = "parquet")]
55use parquet::errors::ParquetError;
56#[cfg(feature = "sql")]
57use sqlparser::parser::ParserError;
58use tokio::task::JoinError;
59
60pub type Result<T, E = DataFusionError> = result::Result<T, E>;
62
63pub type SharedResult<T> = result::Result<T, Arc<DataFusionError>>;
65
66pub type GenericError = Box<dyn Error + Send + Sync>;
68
69#[derive(Debug)]
71pub enum DataFusionError {
72 ArrowError(Box<ArrowError>, Option<String>),
76 #[cfg(feature = "parquet")]
78 ParquetError(Box<ParquetError>),
79 #[cfg(feature = "avro")]
81 AvroError(Box<AvroError>),
82 #[cfg(feature = "object_store")]
84 ObjectStore(Box<object_store::Error>),
85 IoError(io::Error),
87 #[cfg(feature = "sql")]
91 SQL(Box<ParserError>, Option<String>),
92 NotImplemented(String),
98 Internal(String),
115 Plan(String),
121 Configuration(String),
123 SchemaError(Box<SchemaError>, Box<Option<String>>),
131 Execution(String),
138 ExecutionJoin(Box<JoinError>),
142 ResourcesExhausted(String),
147 External(GenericError),
151 Context(String, Box<DataFusionError>),
153 Substrait(String),
156 Diagnostic(Box<Diagnostic>, Box<DataFusionError>),
161 Collection(Vec<DataFusionError>),
168 Shared(Arc<DataFusionError>),
174 Ffi(String),
178}
179
180#[macro_export]
181macro_rules! context {
182 ($desc:expr, $err:expr) => {
183 $err.context(format!("{} at {}:{}", $desc, file!(), line!()))
184 };
185}
186
187#[derive(Debug)]
189pub enum SchemaError {
190 AmbiguousReference { field: Box<Column> },
192 DuplicateQualifiedField {
194 qualifier: Box<TableReference>,
195 name: String,
196 },
197 DuplicateUnqualifiedField { name: String },
199 FieldNotFound {
201 field: Box<Column>,
202 valid_fields: Vec<Column>,
203 },
204}
205
206impl Display for SchemaError {
207 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Self::FieldNotFound {
210 field,
211 valid_fields,
212 } => {
213 write!(f, "No field named {}", field.quoted_flat_name())?;
214 let lower_valid_fields = valid_fields
215 .iter()
216 .map(|column| column.flat_name().to_lowercase())
217 .collect::<Vec<String>>();
218
219 let valid_fields_names = valid_fields
220 .iter()
221 .map(|column| column.flat_name())
222 .collect::<Vec<String>>();
223 if lower_valid_fields.contains(&field.flat_name().to_lowercase()) {
224 write!(
225 f,
226 ". Column names are case sensitive. You can use double quotes to refer to the \"{}\" column \
227 or set the datafusion.sql_parser.enable_ident_normalization configuration",
228 field.quoted_flat_name()
229 )?;
230 }
231 let field_name = field.name();
232 if let Some(matched) = valid_fields_names
233 .iter()
234 .filter(|str| normalized_levenshtein(str, field_name) >= 0.5)
235 .collect::<Vec<&String>>()
236 .first()
237 {
238 write!(f, ". Did you mean '{matched}'?")?;
239 } else if !valid_fields.is_empty() {
240 write!(
241 f,
242 ". Valid fields are {}",
243 valid_fields
244 .iter()
245 .map(|field| field.quoted_flat_name())
246 .collect::<Vec<String>>()
247 .join(", ")
248 )?;
249 }
250 write!(f, ".")
251 }
252 Self::DuplicateQualifiedField { qualifier, name } => {
253 write!(
254 f,
255 "Schema contains duplicate qualified field name {}.{}",
256 qualifier.to_quoted_string(),
257 quote_identifier(name)
258 )
259 }
260 Self::DuplicateUnqualifiedField { name } => {
261 write!(
262 f,
263 "Schema contains duplicate unqualified field name {}",
264 quote_identifier(name)
265 )
266 }
267 Self::AmbiguousReference { field } => {
268 if field.relation.is_some() {
269 write!(
270 f,
271 "Schema contains qualified field name {} and unqualified field name {} which would be ambiguous",
272 field.quoted_flat_name(),
273 quote_identifier(&field.name)
274 )
275 } else {
276 write!(
277 f,
278 "Ambiguous reference to unqualified field {}",
279 field.quoted_flat_name()
280 )
281 }
282 }
283 }
284 }
285}
286
287impl Error for SchemaError {}
288
289impl From<std::fmt::Error> for DataFusionError {
290 fn from(_e: std::fmt::Error) -> Self {
291 DataFusionError::Execution("Fail to format".to_string())
292 }
293}
294
295impl From<io::Error> for DataFusionError {
296 fn from(e: io::Error) -> Self {
297 DataFusionError::IoError(e)
298 }
299}
300
301impl From<ArrowError> for DataFusionError {
302 fn from(e: ArrowError) -> Self {
303 DataFusionError::ArrowError(Box::new(e), Some(DataFusionError::get_back_trace()))
304 }
305}
306
307impl From<DataFusionError> for ArrowError {
308 fn from(e: DataFusionError) -> Self {
309 match e {
310 DataFusionError::ArrowError(e, _) => *e,
311 DataFusionError::External(e) => ArrowError::ExternalError(e),
312 other => ArrowError::ExternalError(Box::new(other)),
313 }
314 }
315}
316
317impl From<&Arc<DataFusionError>> for DataFusionError {
318 fn from(e: &Arc<DataFusionError>) -> Self {
319 if let DataFusionError::Shared(e_inner) = e.as_ref() {
320 DataFusionError::Shared(Arc::clone(e_inner))
322 } else {
323 DataFusionError::Shared(Arc::clone(e))
324 }
325 }
326}
327
328#[cfg(feature = "parquet")]
329impl From<ParquetError> for DataFusionError {
330 fn from(e: ParquetError) -> Self {
331 DataFusionError::ParquetError(Box::new(e))
332 }
333}
334
335#[cfg(feature = "avro")]
336impl From<AvroError> for DataFusionError {
337 fn from(e: AvroError) -> Self {
338 DataFusionError::AvroError(Box::new(e))
339 }
340}
341
342#[cfg(feature = "object_store")]
343impl From<object_store::Error> for DataFusionError {
344 fn from(e: object_store::Error) -> Self {
345 DataFusionError::ObjectStore(Box::new(e))
346 }
347}
348
349#[cfg(feature = "object_store")]
350impl From<object_store::path::Error> for DataFusionError {
351 fn from(e: object_store::path::Error) -> Self {
352 DataFusionError::ObjectStore(Box::new(e.into()))
353 }
354}
355
356#[cfg(feature = "sql")]
357impl From<ParserError> for DataFusionError {
358 fn from(e: ParserError) -> Self {
359 DataFusionError::SQL(Box::new(e), None)
360 }
361}
362
363impl From<GenericError> for DataFusionError {
364 fn from(err: GenericError) -> Self {
365 if err.is::<DataFusionError>() {
367 if let Ok(e) = err.downcast::<DataFusionError>() {
368 *e
369 } else {
370 unreachable!()
371 }
372 } else {
373 DataFusionError::External(err)
374 }
375 }
376}
377
378impl Display for DataFusionError {
379 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
380 let error_prefix = self.error_prefix();
381 let message = self.message();
382 write!(f, "{error_prefix}{message}")
383 }
384}
385
386impl Error for DataFusionError {
387 fn source(&self) -> Option<&(dyn Error + 'static)> {
388 match self {
389 DataFusionError::ArrowError(e, _) => Some(e.as_ref()),
390 #[cfg(feature = "parquet")]
391 DataFusionError::ParquetError(e) => Some(e.as_ref()),
392 #[cfg(feature = "avro")]
393 DataFusionError::AvroError(e) => Some(e.as_ref()),
394 #[cfg(feature = "object_store")]
395 DataFusionError::ObjectStore(e) => Some(e.as_ref()),
396 DataFusionError::IoError(e) => Some(e),
397 #[cfg(feature = "sql")]
398 DataFusionError::SQL(e, _) => Some(e.as_ref()),
399 DataFusionError::NotImplemented(_) => None,
400 DataFusionError::Internal(_) => None,
401 DataFusionError::Configuration(_) => None,
402 DataFusionError::Plan(_) => None,
403 DataFusionError::SchemaError(e, _) => Some(e.as_ref()),
404 DataFusionError::Execution(_) => None,
405 DataFusionError::ExecutionJoin(e) => Some(e.as_ref()),
406 DataFusionError::ResourcesExhausted(_) => None,
407 DataFusionError::External(e) => Some(e.as_ref()),
408 DataFusionError::Context(_, e) => Some(e.as_ref()),
409 DataFusionError::Substrait(_) => None,
410 DataFusionError::Diagnostic(_, e) => Some(e.as_ref()),
411 DataFusionError::Collection(errs) => errs.first().map(|e| e as &dyn Error),
419 DataFusionError::Shared(e) => Some(e.as_ref()),
420 DataFusionError::Ffi(_) => None,
421 }
422 }
423}
424
425impl From<DataFusionError> for io::Error {
426 fn from(e: DataFusionError) -> Self {
427 io::Error::other(e)
428 }
429}
430
431impl DataFusionError {
432 pub const BACK_TRACE_SEP: &'static str = "\n\nbacktrace: ";
434
435 pub fn find_root(&self) -> &Self {
451 let mut last_datafusion_error = self;
455 let mut root_error: &dyn Error = self;
456 while let Some(source) = root_error.source() {
457 root_error = source;
459 if let Some(e) = root_error.downcast_ref::<DataFusionError>() {
461 last_datafusion_error = e;
462 } else if let Some(e) = root_error.downcast_ref::<Arc<DataFusionError>>() {
463 last_datafusion_error = e.as_ref();
466 }
467 }
468 last_datafusion_error
470 }
471
472 pub fn context(self, description: impl Into<String>) -> Self {
474 Self::Context(description.into(), Box::new(self))
475 }
476
477 pub fn strip_backtrace(&self) -> String {
481 (*self
482 .to_string()
483 .split(Self::BACK_TRACE_SEP)
484 .collect::<Vec<&str>>()
485 .first()
486 .unwrap_or(&""))
487 .to_string()
488 }
489
490 #[inline(always)]
498 pub fn get_back_trace() -> String {
499 #[cfg(feature = "backtrace")]
500 {
501 let back_trace = Backtrace::capture();
502 if back_trace.status() == BacktraceStatus::Captured {
503 return format!("{}{}", Self::BACK_TRACE_SEP, back_trace);
504 }
505
506 "".to_owned()
507 }
508
509 #[cfg(not(feature = "backtrace"))]
510 "".to_owned()
511 }
512
513 pub fn builder() -> DataFusionErrorBuilder {
515 DataFusionErrorBuilder::default()
516 }
517
518 fn error_prefix(&self) -> &'static str {
519 match self {
520 DataFusionError::ArrowError(_, _) => "Arrow error: ",
521 #[cfg(feature = "parquet")]
522 DataFusionError::ParquetError(_) => "Parquet error: ",
523 #[cfg(feature = "avro")]
524 DataFusionError::AvroError(_) => "Avro error: ",
525 #[cfg(feature = "object_store")]
526 DataFusionError::ObjectStore(_) => "Object Store error: ",
527 DataFusionError::IoError(_) => "IO error: ",
528 #[cfg(feature = "sql")]
529 DataFusionError::SQL(_, _) => "SQL error: ",
530 DataFusionError::NotImplemented(_) => {
531 "This feature is not implemented: "
532 }
533 DataFusionError::Internal(_) => "Internal error: ",
534 DataFusionError::Plan(_) => "Error during planning: ",
535 DataFusionError::Configuration(_) => {
536 "Invalid or Unsupported Configuration: "
537 }
538 DataFusionError::SchemaError(_, _) => "Schema error: ",
539 DataFusionError::Execution(_) => "Execution error: ",
540 DataFusionError::ExecutionJoin(_) => "ExecutionJoin error: ",
541 DataFusionError::ResourcesExhausted(_) => {
542 "Resources exhausted: "
543 }
544 DataFusionError::External(_) => "External error: ",
545 DataFusionError::Context(_, _) => "",
546 DataFusionError::Substrait(_) => "Substrait error: ",
547 DataFusionError::Diagnostic(_, _) => "",
548 DataFusionError::Collection(errs) => {
549 errs.first().expect("cannot construct DataFusionError::Collection with 0 errors, but got one such case").error_prefix()
550 }
551 DataFusionError::Shared(_) => "",
552 DataFusionError::Ffi(_) => "FFI error: ",
553 }
554 }
555
556 pub fn message(&self) -> Cow<'_, str> {
557 match *self {
558 DataFusionError::ArrowError(ref desc, ref backtrace) => {
559 let backtrace = backtrace.clone().unwrap_or_else(|| "".to_owned());
560 Cow::Owned(format!("{desc}{backtrace}"))
561 }
562 #[cfg(feature = "parquet")]
563 DataFusionError::ParquetError(ref desc) => Cow::Owned(desc.to_string()),
564 #[cfg(feature = "avro")]
565 DataFusionError::AvroError(ref desc) => Cow::Owned(desc.to_string()),
566 DataFusionError::IoError(ref desc) => Cow::Owned(desc.to_string()),
567 #[cfg(feature = "sql")]
568 DataFusionError::SQL(ref desc, ref backtrace) => {
569 let backtrace: String =
570 backtrace.clone().unwrap_or_else(|| "".to_owned());
571 Cow::Owned(format!("{desc:?}{backtrace}"))
572 }
573 DataFusionError::Configuration(ref desc) => Cow::Owned(desc.to_string()),
574 DataFusionError::NotImplemented(ref desc) => Cow::Owned(desc.to_string()),
575 DataFusionError::Internal(ref desc) => Cow::Owned(format!(
576 "{desc}.\nThis issue was likely caused by a bug in DataFusion's code. \
577 Please help us to resolve this by filing a bug report in our issue tracker: \
578 https://github.com/apache/datafusion/issues"
579 )),
580 DataFusionError::Plan(ref desc) => Cow::Owned(desc.to_string()),
581 DataFusionError::SchemaError(ref desc, ref backtrace) => {
582 let backtrace: &str =
583 &backtrace.as_ref().clone().unwrap_or_else(|| "".to_owned());
584 Cow::Owned(format!("{desc}{backtrace}"))
585 }
586 DataFusionError::Execution(ref desc) => Cow::Owned(desc.to_string()),
587 DataFusionError::ExecutionJoin(ref desc) => Cow::Owned(desc.to_string()),
588 DataFusionError::ResourcesExhausted(ref desc) => Cow::Owned(desc.to_string()),
589 DataFusionError::External(ref desc) => Cow::Owned(desc.to_string()),
590 #[cfg(feature = "object_store")]
591 DataFusionError::ObjectStore(ref desc) => Cow::Owned(desc.to_string()),
592 DataFusionError::Context(ref desc, ref err) => {
593 Cow::Owned(format!("{desc}\ncaused by\n{}", *err))
594 }
595 DataFusionError::Substrait(ref desc) => Cow::Owned(desc.to_string()),
596 DataFusionError::Diagnostic(_, ref err) => Cow::Owned(err.to_string()),
597 DataFusionError::Collection(ref errs) => errs
601 .first()
602 .expect("cannot construct DataFusionError::Collection with 0 errors")
603 .message(),
604 DataFusionError::Shared(ref desc) => Cow::Owned(desc.to_string()),
605 DataFusionError::Ffi(ref desc) => Cow::Owned(desc.to_string()),
606 }
607 }
608
609 pub fn with_diagnostic(self, diagnostic: Diagnostic) -> Self {
611 Self::Diagnostic(Box::new(diagnostic), Box::new(self))
612 }
613
614 pub fn with_diagnostic_fn<F: FnOnce(&DataFusionError) -> Diagnostic>(
618 self,
619 f: F,
620 ) -> Self {
621 let diagnostic = f(&self);
622 self.with_diagnostic(diagnostic)
623 }
624
625 pub fn diagnostic(&self) -> Option<&Diagnostic> {
628 struct DiagnosticsIterator<'a> {
629 head: &'a DataFusionError,
630 }
631
632 impl<'a> Iterator for DiagnosticsIterator<'a> {
633 type Item = &'a Diagnostic;
634
635 fn next(&mut self) -> Option<Self::Item> {
636 loop {
637 if let DataFusionError::Diagnostic(diagnostics, source) = self.head {
638 self.head = source.as_ref();
639 return Some(diagnostics);
640 }
641
642 if let Some(source) = self
643 .head
644 .source()
645 .and_then(|source| source.downcast_ref::<DataFusionError>())
646 {
647 self.head = source;
648 } else {
649 return None;
650 }
651 }
652 }
653 }
654
655 DiagnosticsIterator { head: self }.next()
656 }
657
658 pub fn iter(&self) -> impl Iterator<Item = &DataFusionError> {
669 struct ErrorIterator<'a> {
670 queue: VecDeque<&'a DataFusionError>,
671 }
672
673 impl<'a> Iterator for ErrorIterator<'a> {
674 type Item = &'a DataFusionError;
675
676 fn next(&mut self) -> Option<Self::Item> {
677 loop {
678 let popped = self.queue.pop_front()?;
679 match popped {
680 DataFusionError::Collection(errs) => self.queue.extend(errs),
681 _ => return Some(popped),
682 }
683 }
684 }
685 }
686
687 let mut queue = VecDeque::new();
688 queue.push_back(self);
689 ErrorIterator { queue }
690 }
691}
692
693#[derive(Debug, Default)]
718pub struct DataFusionErrorBuilder(Vec<DataFusionError>);
719
720impl DataFusionErrorBuilder {
721 pub fn new() -> Self {
723 Default::default()
724 }
725
726 pub fn add_error(&mut self, error: DataFusionError) {
739 self.0.push(error);
740 }
741
742 pub fn with_error(mut self, error: DataFusionError) -> Self {
755 self.0.push(error);
756 self
757 }
758
759 pub fn error_or<T>(self, ok: T) -> Result<T, DataFusionError> {
762 match self.0.len() {
763 0 => Ok(ok),
764 1 => Err(self.0.into_iter().next().expect("length matched 1")),
765 _ => Err(DataFusionError::Collection(self.0)),
766 }
767 }
768}
769
770#[macro_export]
775macro_rules! unwrap_or_internal_err {
776 ($Value: ident) => {
777 $Value.ok_or_else(|| {
778 $crate::DataFusionError::Internal(format!(
779 "{} should not be None",
780 stringify!($Value)
781 ))
782 })?
783 };
784}
785
786#[macro_export]
796macro_rules! assert_or_internal_err {
797 ($cond:expr) => {
798 if !$cond {
799 return Err($crate::DataFusionError::Internal(format!(
800 "Assertion failed: {}",
801 stringify!($cond)
802 )));
803 }
804 };
805 ($cond:expr, $($arg:tt)+) => {
806 if !$cond {
807 return Err($crate::DataFusionError::Internal(format!(
808 "Assertion failed: {}: {}",
809 stringify!($cond),
810 format!($($arg)+)
811 )));
812 }
813 };
814}
815
816#[macro_export]
826macro_rules! assert_eq_or_internal_err {
827 ($left:expr, $right:expr $(,)?) => {{
828 let left_val = &$left;
829 let right_val = &$right;
830 if left_val != right_val {
831 return Err($crate::DataFusionError::Internal(format!(
832 "Assertion failed: {} == {} (left: {:?}, right: {:?})",
833 stringify!($left),
834 stringify!($right),
835 left_val,
836 right_val
837 )));
838 }
839 }};
840 ($left:expr, $right:expr, $($arg:tt)+) => {{
841 let left_val = &$left;
842 let right_val = &$right;
843 if left_val != right_val {
844 return Err($crate::DataFusionError::Internal(format!(
845 "Assertion failed: {} == {} (left: {:?}, right: {:?}): {}",
846 stringify!($left),
847 stringify!($right),
848 left_val,
849 right_val,
850 format!($($arg)+)
851 )));
852 }
853 }};
854}
855
856#[macro_export]
866macro_rules! assert_ne_or_internal_err {
867 ($left:expr, $right:expr $(,)?) => {{
868 let left_val = &$left;
869 let right_val = &$right;
870 if left_val == right_val {
871 return Err($crate::DataFusionError::Internal(format!(
872 "Assertion failed: {} != {} (left: {:?}, right: {:?})",
873 stringify!($left),
874 stringify!($right),
875 left_val,
876 right_val
877 )));
878 }
879 }};
880 ($left:expr, $right:expr, $($arg:tt)+) => {{
881 let left_val = &$left;
882 let right_val = &$right;
883 if left_val == right_val {
884 return Err($crate::DataFusionError::Internal(format!(
885 "Assertion failed: {} != {} (left: {:?}, right: {:?}): {}",
886 stringify!($left),
887 stringify!($right),
888 left_val,
889 right_val,
890 format!($($arg)+)
891 )));
892 }
893 }};
894}
895
896macro_rules! make_error {
909 ($NAME_ERR:ident, $NAME_DF_ERR: ident, $ERR:ident) => { make_error!(@inner ($), $NAME_ERR, $NAME_DF_ERR, $ERR); };
910 (@inner ($d:tt), $NAME_ERR:ident, $NAME_DF_ERR:ident, $ERR:ident) => {
911 ::paste::paste!{
912 #[macro_export]
914 macro_rules! $NAME_DF_ERR {
915 ($d($d args:expr),* $d(; diagnostic=$d DIAG:expr)?) => {{
916 let err =$crate::DataFusionError::$ERR(
917 ::std::format!(
918 "{}{}",
919 ::std::format!($d($d args),*),
920 $crate::DataFusionError::get_back_trace(),
921 ).into()
922 );
923 $d (
924 let err = err.with_diagnostic($d DIAG);
925 )?
926 err
927 }
928 }
929 }
930
931 #[macro_export]
933 macro_rules! $NAME_ERR {
934 ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
935 let err = $crate::[<_ $NAME_DF_ERR>]!($d($d args),*);
936 $d (
937 let err = err.with_diagnostic($d DIAG);
938 )?
939 Err(err)
940
941 }}
942 }
943
944
945 #[doc(hidden)]
946 pub use $NAME_ERR as [<_ $NAME_ERR>];
947 #[doc(hidden)]
948 pub use $NAME_DF_ERR as [<_ $NAME_DF_ERR>];
949 }
950 };
951}
952
953make_error!(plan_err, plan_datafusion_err, Plan);
955
956make_error!(internal_err, internal_datafusion_err, Internal);
958
959make_error!(not_impl_err, not_impl_datafusion_err, NotImplemented);
961
962make_error!(exec_err, exec_datafusion_err, Execution);
964
965make_error!(config_err, config_datafusion_err, Configuration);
967
968make_error!(substrait_err, substrait_datafusion_err, Substrait);
970
971make_error!(resources_err, resources_datafusion_err, ResourcesExhausted);
973
974make_error!(ffi_err, ffi_datafusion_err, Ffi);
976
977#[macro_export]
979macro_rules! sql_datafusion_err {
980 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
981 let err = $crate::DataFusionError::SQL(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
982 $(
983 let err = err.with_diagnostic($DIAG);
984 )?
985 err
986 }};
987}
988
989#[macro_export]
991macro_rules! sql_err {
992 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
993 let err = $crate::sql_datafusion_err!($ERR);
994 $(
995 let err = err.with_diagnostic($DIAG);
996 )?
997 Err(err)
998 }};
999}
1000
1001#[macro_export]
1003macro_rules! arrow_datafusion_err {
1004 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1005 let err = $crate::DataFusionError::ArrowError(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1006 $(
1007 let err = err.with_diagnostic($DIAG);
1008 )?
1009 err
1010 }};
1011}
1012
1013#[macro_export]
1015macro_rules! arrow_err {
1016 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {
1017 {
1018 let err = $crate::arrow_datafusion_err!($ERR);
1019 $(
1020 let err = err.with_diagnostic($DIAG);
1021 )?
1022 Err(err)
1023 }};
1024}
1025
1026#[macro_export]
1028macro_rules! schema_datafusion_err {
1029 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1030 let err = $crate::DataFusionError::SchemaError(
1031 Box::new($ERR),
1032 Box::new(Some($crate::DataFusionError::get_back_trace())),
1033 );
1034 $(
1035 let err = err.with_diagnostic($DIAG);
1036 )?
1037 err
1038 }};
1039}
1040
1041#[macro_export]
1043macro_rules! schema_err {
1044 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1045 let err = $crate::DataFusionError::SchemaError(
1046 Box::new($ERR),
1047 Box::new(Some($crate::DataFusionError::get_back_trace())),
1048 );
1049 $(
1050 let err = err.with_diagnostic($DIAG);
1051 )?
1052 Err(err)
1053 }
1054 };
1055}
1056
1057pub use schema_err as _schema_err;
1060
1061pub fn field_not_found<R: Into<TableReference>>(
1063 qualifier: Option<R>,
1064 name: &str,
1065 schema: &DFSchema,
1066) -> DataFusionError {
1067 schema_datafusion_err!(SchemaError::FieldNotFound {
1068 field: Box::new(Column::new(qualifier, name)),
1069 valid_fields: schema.columns().to_vec(),
1070 })
1071}
1072
1073pub fn unqualified_field_not_found(name: &str, schema: &DFSchema) -> DataFusionError {
1075 schema_datafusion_err!(SchemaError::FieldNotFound {
1076 field: Box::new(Column::new_unqualified(name)),
1077 valid_fields: schema.columns().to_vec(),
1078 })
1079}
1080
1081pub fn add_possible_columns_to_diag(
1082 diagnostic: &mut Diagnostic,
1083 field: &Column,
1084 valid_fields: &[Column],
1085) {
1086 let field_names: Vec<String> = valid_fields
1087 .iter()
1088 .filter_map(|f| {
1089 if normalized_levenshtein(f.name(), field.name()) >= 0.5 {
1090 Some(f.flat_name())
1091 } else {
1092 None
1093 }
1094 })
1095 .collect();
1096
1097 for name in field_names {
1098 diagnostic.add_note(format!("possible column {name}"), None);
1099 }
1100}
1101
1102#[cfg(test)]
1103mod test {
1104 use super::*;
1105
1106 use std::mem::size_of;
1107 use std::sync::Arc;
1108
1109 use arrow::error::ArrowError;
1110 use insta::assert_snapshot;
1111
1112 fn ok_result() -> Result<()> {
1113 Ok(())
1114 }
1115
1116 #[test]
1117 fn test_assert_eq_or_internal_err_passes() -> Result<()> {
1118 assert_eq_or_internal_err!(1, 1);
1119 ok_result()
1120 }
1121
1122 #[test]
1123 fn test_assert_eq_or_internal_err_fails() {
1124 fn check() -> Result<()> {
1125 assert_eq_or_internal_err!(1, 2, "expected equality");
1126 ok_result()
1127 }
1128
1129 let err = check().unwrap_err();
1130 assert_snapshot!(
1131 err.to_string(),
1132 @r"
1133 Internal error: Assertion failed: 1 == 2 (left: 1, right: 2): expected equality.
1134 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1135 "
1136 );
1137 }
1138
1139 #[test]
1140 fn test_assert_ne_or_internal_err_passes() -> Result<()> {
1141 assert_ne_or_internal_err!(1, 2);
1142 ok_result()
1143 }
1144
1145 #[test]
1146 fn test_assert_ne_or_internal_err_fails() {
1147 fn check() -> Result<()> {
1148 assert_ne_or_internal_err!(3, 3, "values must differ");
1149 ok_result()
1150 }
1151
1152 let err = check().unwrap_err();
1153 assert_snapshot!(
1154 err.to_string(),
1155 @r"
1156 Internal error: Assertion failed: 3 != 3 (left: 3, right: 3): values must differ.
1157 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1158 "
1159 );
1160 }
1161
1162 #[test]
1163 fn test_assert_or_internal_err_passes() -> Result<()> {
1164 assert_or_internal_err!(true);
1165 assert_or_internal_err!(true, "message");
1166 ok_result()
1167 }
1168
1169 #[test]
1170 fn test_assert_or_internal_err_fails_default() {
1171 fn check() -> Result<()> {
1172 assert_or_internal_err!(false);
1173 ok_result()
1174 }
1175
1176 let err = check().unwrap_err();
1177 assert_snapshot!(
1178 err.to_string(),
1179 @r"
1180 Internal error: Assertion failed: false.
1181 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1182 "
1183 );
1184 }
1185
1186 #[test]
1187 fn test_assert_or_internal_err_fails_with_message() {
1188 fn check() -> Result<()> {
1189 assert_or_internal_err!(false, "custom message");
1190 ok_result()
1191 }
1192
1193 let err = check().unwrap_err();
1194 assert_snapshot!(
1195 err.to_string(),
1196 @r"
1197 Internal error: Assertion failed: false: custom message.
1198 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1199 "
1200 );
1201 }
1202
1203 #[test]
1204 fn test_assert_or_internal_err_with_format_arguments() {
1205 fn check() -> Result<()> {
1206 assert_or_internal_err!(false, "custom {}", 42);
1207 ok_result()
1208 }
1209
1210 let err = check().unwrap_err();
1211 assert_snapshot!(
1212 err.to_string(),
1213 @r"
1214 Internal error: Assertion failed: false: custom 42.
1215 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1216 "
1217 );
1218 }
1219
1220 #[test]
1221 fn test_error_size() {
1222 assert_eq!(size_of::<SchemaError>(), 40);
1225 assert_eq!(size_of::<DataFusionError>(), 40);
1226 }
1227
1228 #[test]
1229 fn datafusion_error_to_arrow() {
1230 let res = return_arrow_error().unwrap_err();
1231 assert!(
1232 res.to_string()
1233 .starts_with("External error: Error during planning: foo")
1234 );
1235 }
1236
1237 #[test]
1238 fn arrow_error_to_datafusion() {
1239 let res = return_datafusion_error().unwrap_err();
1240 assert_eq!(res.strip_backtrace(), "Arrow error: Schema error: bar");
1241 }
1242
1243 #[cfg(feature = "backtrace")]
1245 #[test]
1246 #[expect(clippy::unnecessary_literal_unwrap)]
1247 fn test_enabled_backtrace() {
1248 match std::env::var("RUST_BACKTRACE") {
1249 Ok(val) if val == "1" => {}
1250 _ => panic!("Environment variable RUST_BACKTRACE must be set to 1"),
1251 };
1252
1253 let res: Result<(), DataFusionError> = plan_err!("Err");
1254 let err = res.unwrap_err().to_string();
1255 assert!(err.contains(DataFusionError::BACK_TRACE_SEP));
1256 assert_eq!(
1257 err.split(DataFusionError::BACK_TRACE_SEP)
1258 .collect::<Vec<&str>>()
1259 .first()
1260 .unwrap(),
1261 &"Error during planning: Err"
1262 );
1263 assert!(
1264 !err.split(DataFusionError::BACK_TRACE_SEP)
1265 .collect::<Vec<&str>>()
1266 .get(1)
1267 .unwrap()
1268 .is_empty()
1269 );
1270 }
1271
1272 #[cfg(not(feature = "backtrace"))]
1273 #[test]
1274 fn test_disabled_backtrace() {
1275 let res: Result<(), DataFusionError> = plan_err!("Err");
1276 let res = res.unwrap_err().to_string();
1277 assert!(!res.contains(DataFusionError::BACK_TRACE_SEP));
1278 assert_eq!(res, "Error during planning: Err");
1279 }
1280
1281 #[test]
1282 fn test_find_root_error() {
1283 do_root_test(
1284 DataFusionError::Context(
1285 "it happened!".to_string(),
1286 Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1287 ),
1288 DataFusionError::ResourcesExhausted("foo".to_string()),
1289 );
1290
1291 do_root_test(
1292 DataFusionError::ArrowError(
1293 Box::new(ArrowError::ExternalError(Box::new(
1294 DataFusionError::ResourcesExhausted("foo".to_string()),
1295 ))),
1296 None,
1297 ),
1298 DataFusionError::ResourcesExhausted("foo".to_string()),
1299 );
1300
1301 do_root_test(
1302 DataFusionError::External(Box::new(DataFusionError::ResourcesExhausted(
1303 "foo".to_string(),
1304 ))),
1305 DataFusionError::ResourcesExhausted("foo".to_string()),
1306 );
1307
1308 do_root_test(
1309 DataFusionError::External(Box::new(ArrowError::ExternalError(Box::new(
1310 DataFusionError::ResourcesExhausted("foo".to_string()),
1311 )))),
1312 DataFusionError::ResourcesExhausted("foo".to_string()),
1313 );
1314
1315 do_root_test(
1316 DataFusionError::ArrowError(
1317 Box::new(ArrowError::ExternalError(Box::new(
1318 ArrowError::ExternalError(Box::new(
1319 DataFusionError::ResourcesExhausted("foo".to_string()),
1320 )),
1321 ))),
1322 None,
1323 ),
1324 DataFusionError::ResourcesExhausted("foo".to_string()),
1325 );
1326
1327 do_root_test(
1328 DataFusionError::External(Box::new(Arc::new(
1329 DataFusionError::ResourcesExhausted("foo".to_string()),
1330 ))),
1331 DataFusionError::ResourcesExhausted("foo".to_string()),
1332 );
1333
1334 do_root_test(
1335 DataFusionError::External(Box::new(Arc::new(ArrowError::ExternalError(
1336 Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1337 )))),
1338 DataFusionError::ResourcesExhausted("foo".to_string()),
1339 );
1340 }
1341
1342 #[test]
1343 fn test_make_error_parse_input() {
1344 let res: Result<(), DataFusionError> = plan_err!("Err");
1345 let res = res.unwrap_err();
1346 assert_eq!(res.strip_backtrace(), "Error during planning: Err");
1347
1348 let extra1 = "extra1";
1349 let extra2 = "extra2";
1350
1351 let res: Result<(), DataFusionError> = plan_err!("Err {} {}", extra1, extra2);
1352 let res = res.unwrap_err();
1353 assert_eq!(
1354 res.strip_backtrace(),
1355 "Error during planning: Err extra1 extra2"
1356 );
1357
1358 let res: Result<(), DataFusionError> =
1359 plan_err!("Err {:?} {:#?}", extra1, extra2);
1360 let res = res.unwrap_err();
1361 assert_eq!(
1362 res.strip_backtrace(),
1363 "Error during planning: Err \"extra1\" \"extra2\""
1364 );
1365
1366 let res: Result<(), DataFusionError> = plan_err!("Err {extra1} {extra2}");
1367 let res = res.unwrap_err();
1368 assert_eq!(
1369 res.strip_backtrace(),
1370 "Error during planning: Err extra1 extra2"
1371 );
1372
1373 let res: Result<(), DataFusionError> = plan_err!("Err {extra1:?} {extra2:#?}");
1374 let res = res.unwrap_err();
1375 assert_eq!(
1376 res.strip_backtrace(),
1377 "Error during planning: Err \"extra1\" \"extra2\""
1378 );
1379 }
1380
1381 #[test]
1382 fn external_error() {
1383 let generic_error: GenericError =
1385 Box::new(DataFusionError::Plan("test".to_string()));
1386 let datafusion_error: DataFusionError = generic_error.into();
1387 println!("{}", datafusion_error.strip_backtrace());
1388 assert_eq!(
1389 datafusion_error.strip_backtrace(),
1390 "Error during planning: test"
1391 );
1392
1393 let generic_error: GenericError = Box::new(io::Error::other("io error"));
1395 let datafusion_error: DataFusionError = generic_error.into();
1396 println!("{}", datafusion_error.strip_backtrace());
1397 assert_eq!(
1398 datafusion_error.strip_backtrace(),
1399 "External error: io error"
1400 );
1401 }
1402
1403 #[test]
1404 fn external_error_no_recursive() {
1405 let generic_error_1: GenericError = Box::new(io::Error::other("io error"));
1406 let external_error_1: DataFusionError = generic_error_1.into();
1407 let generic_error_2: GenericError = Box::new(external_error_1);
1408 let external_error_2: DataFusionError = generic_error_2.into();
1409
1410 println!("{external_error_2}");
1411 assert!(
1412 external_error_2
1413 .to_string()
1414 .starts_with("External error: io error")
1415 );
1416 }
1417
1418 fn return_arrow_error() -> arrow::error::Result<()> {
1421 Err(DataFusionError::Plan("foo".to_string()).into())
1423 }
1424
1425 fn return_datafusion_error() -> Result<()> {
1428 Err(ArrowError::SchemaError("bar".to_string()).into())
1430 }
1431
1432 fn do_root_test(e: DataFusionError, exp: DataFusionError) {
1433 let e = e.find_root();
1434
1435 assert_eq!(e.strip_backtrace(), exp.strip_backtrace());
1437 assert_eq!(std::mem::discriminant(e), std::mem::discriminant(&exp),)
1438 }
1439
1440 #[test]
1441 fn test_iter() {
1442 let err = DataFusionError::Collection(vec![
1443 DataFusionError::Plan("a".to_string()),
1444 DataFusionError::Collection(vec![
1445 DataFusionError::Plan("b".to_string()),
1446 DataFusionError::Plan("c".to_string()),
1447 ]),
1448 ]);
1449 let errs = err.iter().collect::<Vec<_>>();
1450 assert_eq!(errs.len(), 3);
1451 assert_eq!(errs[0].strip_backtrace(), "Error during planning: a");
1452 assert_eq!(errs[1].strip_backtrace(), "Error during planning: b");
1453 assert_eq!(errs[2].strip_backtrace(), "Error during planning: c");
1454 }
1455}