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};
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
58pub type Result<T, E = DataFusionError> = result::Result<T, E>;
60
61pub type SharedResult<T> = result::Result<T, Arc<DataFusionError>>;
63
64pub type GenericError = Box<dyn Error + Send + Sync>;
66
67#[derive(Debug)]
69pub enum DataFusionError {
70 ArrowError(Box<ArrowError>, Option<String>),
74 #[cfg(feature = "parquet")]
76 ParquetError(Box<ParquetError>),
77 #[cfg(feature = "object_store")]
79 ObjectStore(Box<object_store::Error>),
80 IoError(io::Error),
82 #[cfg(feature = "sql")]
86 SQL(Box<ParserError>, Option<String>),
87 NotImplemented(String),
93 Internal(String),
110 Plan(String),
116 Configuration(String),
118 SchemaError(Box<SchemaError>, Box<Option<String>>),
126 Execution(String),
133 ExecutionJoin(Box<JoinError>),
137 ResourcesExhausted(String),
142 External(GenericError),
146 Context(String, Box<DataFusionError>),
148 Substrait(String),
151 Diagnostic(Box<Diagnostic>, Box<DataFusionError>),
156 Collection(Vec<DataFusionError>),
163 Shared(Arc<DataFusionError>),
169 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#[derive(Debug)]
184pub enum SchemaError {
185 AmbiguousReference { field: Box<Column> },
187 DuplicateQualifiedField {
189 qualifier: Box<TableReference>,
190 name: String,
191 },
192 DuplicateUnqualifiedField { name: String },
194 FieldNotFound {
196 field: Box<Column>,
197 valid_fields: Vec<Column>,
198 },
199}
200
201impl Display for SchemaError {
202 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203 match self {
204 Self::FieldNotFound {
205 field,
206 valid_fields,
207 } => {
208 write!(f, "No field named {}", field.quoted_flat_name())?;
209 let lower_valid_fields = valid_fields
210 .iter()
211 .map(|column| column.flat_name().to_lowercase())
212 .collect::<Vec<String>>();
213
214 let valid_fields_names = valid_fields
215 .iter()
216 .map(|column| column.flat_name())
217 .collect::<Vec<String>>();
218 if lower_valid_fields.contains(&field.flat_name().to_lowercase()) {
219 write!(
220 f,
221 ". Column names are case sensitive. You can use double quotes to refer to the \"{}\" column \
222 or set the datafusion.sql_parser.enable_ident_normalization configuration",
223 field.quoted_flat_name()
224 )?;
225 }
226 let field_name = field.name();
227 if let Some(matched) = valid_fields_names
228 .iter()
229 .filter(|str| normalized_levenshtein(str, field_name) >= 0.5)
230 .collect::<Vec<&String>>()
231 .first()
232 {
233 write!(f, ". Did you mean '{matched}'?")?;
234 } else if !valid_fields.is_empty() {
235 write!(
236 f,
237 ". Valid fields are {}",
238 valid_fields
239 .iter()
240 .map(|field| field.quoted_flat_name())
241 .collect::<Vec<String>>()
242 .join(", ")
243 )?;
244 }
245 write!(f, ".")
246 }
247 Self::DuplicateQualifiedField { qualifier, name } => {
248 write!(
249 f,
250 "Schema contains duplicate qualified field name {}.{}",
251 qualifier.to_quoted_string(),
252 quote_identifier(name)
253 )
254 }
255 Self::DuplicateUnqualifiedField { name } => {
256 write!(
257 f,
258 "Schema contains duplicate unqualified field name {}",
259 quote_identifier(name)
260 )
261 }
262 Self::AmbiguousReference { field } => {
263 if field.relation.is_some() {
264 write!(
265 f,
266 "Schema contains qualified field name {} and unqualified field name {} which would be ambiguous",
267 field.quoted_flat_name(),
268 quote_identifier(&field.name)
269 )
270 } else {
271 write!(
272 f,
273 "Ambiguous reference to unqualified field {}",
274 field.quoted_flat_name()
275 )
276 }
277 }
278 }
279 }
280}
281
282impl Error for SchemaError {}
283
284impl From<std::fmt::Error> for DataFusionError {
285 fn from(_e: std::fmt::Error) -> Self {
286 DataFusionError::Execution("Fail to format".to_string())
287 }
288}
289
290impl From<io::Error> for DataFusionError {
291 fn from(e: io::Error) -> Self {
292 DataFusionError::IoError(e)
293 }
294}
295
296impl From<ArrowError> for DataFusionError {
297 fn from(e: ArrowError) -> Self {
298 DataFusionError::ArrowError(Box::new(e), Some(DataFusionError::get_back_trace()))
299 }
300}
301
302impl From<DataFusionError> for ArrowError {
303 fn from(e: DataFusionError) -> Self {
304 match e {
305 DataFusionError::ArrowError(e, _) => *e,
306 DataFusionError::External(e) => ArrowError::ExternalError(e),
307 other => ArrowError::ExternalError(Box::new(other)),
308 }
309 }
310}
311
312impl From<&Arc<DataFusionError>> for DataFusionError {
313 fn from(e: &Arc<DataFusionError>) -> Self {
314 if let DataFusionError::Shared(e_inner) = e.as_ref() {
315 DataFusionError::Shared(Arc::clone(e_inner))
317 } else {
318 DataFusionError::Shared(Arc::clone(e))
319 }
320 }
321}
322
323#[cfg(feature = "parquet")]
324impl From<ParquetError> for DataFusionError {
325 fn from(e: ParquetError) -> Self {
326 DataFusionError::ParquetError(Box::new(e))
327 }
328}
329
330#[cfg(feature = "object_store")]
331impl From<object_store::Error> for DataFusionError {
332 fn from(e: object_store::Error) -> Self {
333 DataFusionError::ObjectStore(Box::new(e))
334 }
335}
336
337#[cfg(feature = "object_store")]
338impl From<object_store::path::Error> for DataFusionError {
339 fn from(e: object_store::path::Error) -> Self {
340 DataFusionError::ObjectStore(Box::new(e.into()))
341 }
342}
343
344#[cfg(feature = "sql")]
345impl From<ParserError> for DataFusionError {
346 fn from(e: ParserError) -> Self {
347 DataFusionError::SQL(Box::new(e), None)
348 }
349}
350
351impl From<GenericError> for DataFusionError {
352 fn from(err: GenericError) -> Self {
353 if err.is::<DataFusionError>() {
355 if let Ok(e) = err.downcast::<DataFusionError>() {
356 *e
357 } else {
358 unreachable!()
359 }
360 } else {
361 DataFusionError::External(err)
362 }
363 }
364}
365
366impl Display for DataFusionError {
367 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
368 let error_prefix = self.error_prefix();
369 let message = self.message();
370 write!(f, "{error_prefix}{message}")
371 }
372}
373
374impl Error for DataFusionError {
375 fn source(&self) -> Option<&(dyn Error + 'static)> {
376 match self {
377 DataFusionError::ArrowError(e, _) => Some(e.as_ref()),
378 #[cfg(feature = "parquet")]
379 DataFusionError::ParquetError(e) => Some(e.as_ref()),
380 #[cfg(feature = "object_store")]
381 DataFusionError::ObjectStore(e) => Some(e.as_ref()),
382 DataFusionError::IoError(e) => Some(e),
383 #[cfg(feature = "sql")]
384 DataFusionError::SQL(e, _) => Some(e.as_ref()),
385 DataFusionError::NotImplemented(_) => None,
386 DataFusionError::Internal(_) => None,
387 DataFusionError::Configuration(_) => None,
388 DataFusionError::Plan(_) => None,
389 DataFusionError::SchemaError(e, _) => Some(e.as_ref()),
390 DataFusionError::Execution(_) => None,
391 DataFusionError::ExecutionJoin(e) => Some(e.as_ref()),
392 DataFusionError::ResourcesExhausted(_) => None,
393 DataFusionError::External(e) => Some(e.as_ref()),
394 DataFusionError::Context(_, e) => Some(e.as_ref()),
395 DataFusionError::Substrait(_) => None,
396 DataFusionError::Diagnostic(_, e) => Some(e.as_ref()),
397 DataFusionError::Collection(errs) => errs.first().map(|e| e as &dyn Error),
405 DataFusionError::Shared(e) => Some(e.as_ref()),
406 DataFusionError::Ffi(_) => None,
407 }
408 }
409}
410
411impl From<DataFusionError> for io::Error {
412 fn from(e: DataFusionError) -> Self {
413 io::Error::other(e)
414 }
415}
416
417impl DataFusionError {
418 pub const BACK_TRACE_SEP: &'static str = "\n\nbacktrace: ";
420
421 pub fn find_root(&self) -> &Self {
437 let mut last_datafusion_error = self;
441 let mut root_error: &dyn Error = self;
442 while let Some(source) = root_error.source() {
443 root_error = source;
445 if let Some(e) = root_error.downcast_ref::<DataFusionError>() {
447 last_datafusion_error = e;
448 } else if let Some(e) = root_error.downcast_ref::<Arc<DataFusionError>>() {
449 last_datafusion_error = e.as_ref();
452 }
453 }
454 last_datafusion_error
456 }
457
458 pub fn context(self, description: impl Into<String>) -> Self {
460 Self::Context(description.into(), Box::new(self))
461 }
462
463 pub fn strip_backtrace(&self) -> String {
467 (*self
468 .to_string()
469 .split(Self::BACK_TRACE_SEP)
470 .collect::<Vec<&str>>()
471 .first()
472 .unwrap_or(&""))
473 .to_string()
474 }
475
476 #[inline(always)]
484 pub fn get_back_trace() -> String {
485 #[cfg(feature = "backtrace")]
486 {
487 let back_trace = Backtrace::capture();
488 if back_trace.status() == BacktraceStatus::Captured {
489 return format!("{}{}", Self::BACK_TRACE_SEP, back_trace);
490 }
491
492 "".to_owned()
493 }
494
495 #[cfg(not(feature = "backtrace"))]
496 "".to_owned()
497 }
498
499 pub fn builder() -> DataFusionErrorBuilder {
501 DataFusionErrorBuilder::default()
502 }
503
504 fn error_prefix(&self) -> &'static str {
505 match self {
506 DataFusionError::ArrowError(_, _) => "Arrow error: ",
507 #[cfg(feature = "parquet")]
508 DataFusionError::ParquetError(_) => "Parquet error: ",
509 #[cfg(feature = "object_store")]
510 DataFusionError::ObjectStore(_) => "Object Store error: ",
511 DataFusionError::IoError(_) => "IO error: ",
512 #[cfg(feature = "sql")]
513 DataFusionError::SQL(_, _) => "SQL error: ",
514 DataFusionError::NotImplemented(_) => {
515 "This feature is not implemented: "
516 }
517 DataFusionError::Internal(_) => "Internal error: ",
518 DataFusionError::Plan(_) => "Error during planning: ",
519 DataFusionError::Configuration(_) => {
520 "Invalid or Unsupported Configuration: "
521 }
522 DataFusionError::SchemaError(_, _) => "Schema error: ",
523 DataFusionError::Execution(_) => "Execution error: ",
524 DataFusionError::ExecutionJoin(_) => "ExecutionJoin error: ",
525 DataFusionError::ResourcesExhausted(_) => {
526 "Resources exhausted: "
527 }
528 DataFusionError::External(_) => "External error: ",
529 DataFusionError::Context(_, _) => "",
530 DataFusionError::Substrait(_) => "Substrait error: ",
531 DataFusionError::Diagnostic(_, _) => "",
532 DataFusionError::Collection(errs) => {
533 errs.first().expect("cannot construct DataFusionError::Collection with 0 errors, but got one such case").error_prefix()
534 }
535 DataFusionError::Shared(_) => "",
536 DataFusionError::Ffi(_) => "FFI error: ",
537 }
538 }
539
540 pub fn message(&self) -> Cow<'_, str> {
541 match *self {
542 DataFusionError::ArrowError(ref desc, ref backtrace) => {
543 let backtrace = backtrace.clone().unwrap_or_else(|| "".to_owned());
544 Cow::Owned(format!("{desc}{backtrace}"))
545 }
546 #[cfg(feature = "parquet")]
547 DataFusionError::ParquetError(ref desc) => Cow::Owned(desc.to_string()),
548 DataFusionError::IoError(ref desc) => Cow::Owned(desc.to_string()),
549 #[cfg(feature = "sql")]
550 DataFusionError::SQL(ref desc, ref backtrace) => {
551 let backtrace: String =
552 backtrace.clone().unwrap_or_else(|| "".to_owned());
553 Cow::Owned(format!("{desc:?}{backtrace}"))
554 }
555 DataFusionError::Configuration(ref desc) => Cow::Owned(desc.to_string()),
556 DataFusionError::NotImplemented(ref desc) => Cow::Owned(desc.to_string()),
557 DataFusionError::Internal(ref desc) => Cow::Owned(format!(
558 "{desc}.\nThis issue was likely caused by a bug in DataFusion's code. \
559 Please help us to resolve this by filing a bug report in our issue tracker: \
560 https://github.com/apache/datafusion/issues"
561 )),
562 DataFusionError::Plan(ref desc) => Cow::Owned(desc.to_string()),
563 DataFusionError::SchemaError(ref desc, ref backtrace) => {
564 let backtrace: &str =
565 &backtrace.as_ref().clone().unwrap_or_else(|| "".to_owned());
566 Cow::Owned(format!("{desc}{backtrace}"))
567 }
568 DataFusionError::Execution(ref desc) => Cow::Owned(desc.to_string()),
569 DataFusionError::ExecutionJoin(ref desc) => Cow::Owned(desc.to_string()),
570 DataFusionError::ResourcesExhausted(ref desc) => Cow::Owned(desc.to_string()),
571 DataFusionError::External(ref desc) => Cow::Owned(desc.to_string()),
572 #[cfg(feature = "object_store")]
573 DataFusionError::ObjectStore(ref desc) => Cow::Owned(desc.to_string()),
574 DataFusionError::Context(ref desc, ref err) => {
575 Cow::Owned(format!("{desc}\ncaused by\n{}", *err))
576 }
577 DataFusionError::Substrait(ref desc) => Cow::Owned(desc.to_string()),
578 DataFusionError::Diagnostic(_, ref err) => Cow::Owned(err.to_string()),
579 DataFusionError::Collection(ref errs) => errs
583 .first()
584 .expect("cannot construct DataFusionError::Collection with 0 errors")
585 .message(),
586 DataFusionError::Shared(ref desc) => Cow::Owned(desc.to_string()),
587 DataFusionError::Ffi(ref desc) => Cow::Owned(desc.to_string()),
588 }
589 }
590
591 pub fn with_diagnostic(self, diagnostic: Diagnostic) -> Self {
593 Self::Diagnostic(Box::new(diagnostic), Box::new(self))
594 }
595
596 pub fn with_diagnostic_fn<F: FnOnce(&DataFusionError) -> Diagnostic>(
600 self,
601 f: F,
602 ) -> Self {
603 let diagnostic = f(&self);
604 self.with_diagnostic(diagnostic)
605 }
606
607 pub fn diagnostic(&self) -> Option<&Diagnostic> {
610 struct DiagnosticsIterator<'a> {
611 head: &'a DataFusionError,
612 }
613
614 impl<'a> Iterator for DiagnosticsIterator<'a> {
615 type Item = &'a Diagnostic;
616
617 fn next(&mut self) -> Option<Self::Item> {
618 loop {
619 if let DataFusionError::Diagnostic(diagnostics, source) = self.head {
620 self.head = source.as_ref();
621 return Some(diagnostics);
622 }
623
624 if let Some(source) = self
625 .head
626 .source()
627 .and_then(|source| source.downcast_ref::<DataFusionError>())
628 {
629 self.head = source;
630 } else {
631 return None;
632 }
633 }
634 }
635 }
636
637 DiagnosticsIterator { head: self }.next()
638 }
639
640 pub fn iter(&self) -> impl Iterator<Item = &DataFusionError> {
651 struct ErrorIterator<'a> {
652 queue: VecDeque<&'a DataFusionError>,
653 }
654
655 impl<'a> Iterator for ErrorIterator<'a> {
656 type Item = &'a DataFusionError;
657
658 fn next(&mut self) -> Option<Self::Item> {
659 loop {
660 let popped = self.queue.pop_front()?;
661 match popped {
662 DataFusionError::Collection(errs) => self.queue.extend(errs),
663 _ => return Some(popped),
664 }
665 }
666 }
667 }
668
669 let mut queue = VecDeque::new();
670 queue.push_back(self);
671 ErrorIterator { queue }
672 }
673}
674
675#[derive(Debug, Default)]
700pub struct DataFusionErrorBuilder(Vec<DataFusionError>);
701
702impl DataFusionErrorBuilder {
703 pub fn new() -> Self {
705 Default::default()
706 }
707
708 pub fn add_error(&mut self, error: DataFusionError) {
721 self.0.push(error);
722 }
723
724 pub fn with_error(mut self, error: DataFusionError) -> Self {
737 self.0.push(error);
738 self
739 }
740
741 pub fn error_or<T>(self, ok: T) -> Result<T, DataFusionError> {
744 match self.0.len() {
745 0 => Ok(ok),
746 1 => Err(self.0.into_iter().next().expect("length matched 1")),
747 _ => Err(DataFusionError::Collection(self.0)),
748 }
749 }
750}
751
752#[macro_export]
757macro_rules! unwrap_or_internal_err {
758 ($Value: ident) => {
759 $Value.ok_or_else(|| {
760 $crate::DataFusionError::Internal(format!(
761 "{} should not be None",
762 stringify!($Value)
763 ))
764 })?
765 };
766}
767
768#[macro_export]
778macro_rules! assert_or_internal_err {
779 ($cond:expr) => {
780 if !$cond {
781 return Err($crate::DataFusionError::Internal(format!(
782 "Assertion failed: {}",
783 stringify!($cond)
784 )));
785 }
786 };
787 ($cond:expr, $($arg:tt)+) => {
788 if !$cond {
789 return Err($crate::DataFusionError::Internal(format!(
790 "Assertion failed: {}: {}",
791 stringify!($cond),
792 format!($($arg)+)
793 )));
794 }
795 };
796}
797
798#[macro_export]
808macro_rules! assert_eq_or_internal_err {
809 ($left:expr, $right:expr $(,)?) => {{
810 let left_val = &$left;
811 let right_val = &$right;
812 if left_val != right_val {
813 return Err($crate::DataFusionError::Internal(format!(
814 "Assertion failed: {} == {} (left: {:?}, right: {:?})",
815 stringify!($left),
816 stringify!($right),
817 left_val,
818 right_val
819 )));
820 }
821 }};
822 ($left:expr, $right:expr, $($arg:tt)+) => {{
823 let left_val = &$left;
824 let right_val = &$right;
825 if left_val != right_val {
826 return Err($crate::DataFusionError::Internal(format!(
827 "Assertion failed: {} == {} (left: {:?}, right: {:?}): {}",
828 stringify!($left),
829 stringify!($right),
830 left_val,
831 right_val,
832 format!($($arg)+)
833 )));
834 }
835 }};
836}
837
838#[macro_export]
848macro_rules! assert_ne_or_internal_err {
849 ($left:expr, $right:expr $(,)?) => {{
850 let left_val = &$left;
851 let right_val = &$right;
852 if left_val == right_val {
853 return Err($crate::DataFusionError::Internal(format!(
854 "Assertion failed: {} != {} (left: {:?}, right: {:?})",
855 stringify!($left),
856 stringify!($right),
857 left_val,
858 right_val
859 )));
860 }
861 }};
862 ($left:expr, $right:expr, $($arg:tt)+) => {{
863 let left_val = &$left;
864 let right_val = &$right;
865 if left_val == right_val {
866 return Err($crate::DataFusionError::Internal(format!(
867 "Assertion failed: {} != {} (left: {:?}, right: {:?}): {}",
868 stringify!($left),
869 stringify!($right),
870 left_val,
871 right_val,
872 format!($($arg)+)
873 )));
874 }
875 }};
876}
877
878macro_rules! make_error {
895 ($NAME_ERR:ident, $PREFIXED_NAME_ERR:ident, $NAME_DF_ERR:ident, $PREFIXED_NAME_DF_ERR:ident, $ERR:ident) => {
896 make_error!(@inner ($), $NAME_ERR, $PREFIXED_NAME_ERR, $NAME_DF_ERR, $PREFIXED_NAME_DF_ERR, $ERR);
897 };
898 (@inner ($d:tt), $NAME_ERR:ident, $PREFIXED_NAME_ERR:ident, $NAME_DF_ERR:ident, $PREFIXED_NAME_DF_ERR:ident, $ERR:ident) => {
899 #[macro_export]
901 macro_rules! $NAME_DF_ERR {
902 ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
903 let err = $crate::DataFusionError::$ERR(
904 ::std::format!(
905 "{}{}",
906 ::std::format!($d($d args),*),
907 $crate::DataFusionError::get_back_trace(),
908 ).into()
909 );
910 $d (
911 let err = err.with_diagnostic($d DIAG);
912 )?
913 err
914 }}
915 }
916
917 #[macro_export]
919 macro_rules! $NAME_ERR {
920 ($d($d args:expr),* $d(; diagnostic = $d DIAG:expr)?) => {{
921 let err = $crate::$PREFIXED_NAME_DF_ERR!($d($d args),*);
922 $d (
923 let err = err.with_diagnostic($d DIAG);
924 )?
925 Err(err)
926 }}
927 }
928
929 #[doc(hidden)]
930 pub use $NAME_ERR as $PREFIXED_NAME_ERR;
931 #[doc(hidden)]
932 pub use $NAME_DF_ERR as $PREFIXED_NAME_DF_ERR;
933 };
934}
935
936make_error!(
938 plan_err,
939 _plan_err,
940 plan_datafusion_err,
941 _plan_datafusion_err,
942 Plan
943);
944
945make_error!(
947 internal_err,
948 _internal_err,
949 internal_datafusion_err,
950 _internal_datafusion_err,
951 Internal
952);
953
954make_error!(
956 not_impl_err,
957 _not_impl_err,
958 not_impl_datafusion_err,
959 _not_impl_datafusion_err,
960 NotImplemented
961);
962
963make_error!(
965 exec_err,
966 _exec_err,
967 exec_datafusion_err,
968 _exec_datafusion_err,
969 Execution
970);
971
972make_error!(
974 config_err,
975 _config_err,
976 config_datafusion_err,
977 _config_datafusion_err,
978 Configuration
979);
980
981make_error!(
983 substrait_err,
984 _substrait_err,
985 substrait_datafusion_err,
986 _substrait_datafusion_err,
987 Substrait
988);
989
990make_error!(
992 resources_err,
993 _resources_err,
994 resources_datafusion_err,
995 _resources_datafusion_err,
996 ResourcesExhausted
997);
998
999make_error!(
1001 ffi_err,
1002 _ffi_err,
1003 ffi_datafusion_err,
1004 _ffi_datafusion_err,
1005 Ffi
1006);
1007
1008#[macro_export]
1010macro_rules! sql_datafusion_err {
1011 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1012 let err = $crate::DataFusionError::SQL(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1013 $(
1014 let err = err.with_diagnostic($DIAG);
1015 )?
1016 err
1017 }};
1018}
1019
1020#[macro_export]
1022macro_rules! sql_err {
1023 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1024 let err = $crate::sql_datafusion_err!($ERR);
1025 $(
1026 let err = err.with_diagnostic($DIAG);
1027 )?
1028 Err(err)
1029 }};
1030}
1031
1032#[macro_export]
1034macro_rules! arrow_datafusion_err {
1035 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1036 let err = $crate::DataFusionError::ArrowError(Box::new($ERR), Some($crate::DataFusionError::get_back_trace()));
1037 $(
1038 let err = err.with_diagnostic($DIAG);
1039 )?
1040 err
1041 }};
1042}
1043
1044#[macro_export]
1046macro_rules! arrow_err {
1047 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {
1048 {
1049 let err = $crate::arrow_datafusion_err!($ERR);
1050 $(
1051 let err = err.with_diagnostic($DIAG);
1052 )?
1053 Err(err)
1054 }};
1055}
1056
1057#[macro_export]
1059macro_rules! schema_datafusion_err {
1060 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1061 let err = $crate::DataFusionError::SchemaError(
1062 Box::new($ERR),
1063 Box::new(Some($crate::DataFusionError::get_back_trace())),
1064 );
1065 $(
1066 let err = err.with_diagnostic($DIAG);
1067 )?
1068 err
1069 }};
1070}
1071
1072#[macro_export]
1074macro_rules! schema_err {
1075 ($ERR:expr $(; diagnostic = $DIAG:expr)?) => {{
1076 let err = $crate::DataFusionError::SchemaError(
1077 Box::new($ERR),
1078 Box::new(Some($crate::DataFusionError::get_back_trace())),
1079 );
1080 $(
1081 let err = err.with_diagnostic($DIAG);
1082 )?
1083 Err(err)
1084 }
1085 };
1086}
1087
1088pub use schema_err as _schema_err;
1091
1092pub fn field_not_found<R: Into<TableReference>>(
1094 qualifier: Option<R>,
1095 name: &str,
1096 schema: &DFSchema,
1097) -> DataFusionError {
1098 schema_datafusion_err!(SchemaError::FieldNotFound {
1099 field: Box::new(Column::new(qualifier, name)),
1100 valid_fields: schema.columns().to_vec(),
1101 })
1102}
1103
1104pub fn unqualified_field_not_found(name: &str, schema: &DFSchema) -> DataFusionError {
1106 schema_datafusion_err!(SchemaError::FieldNotFound {
1107 field: Box::new(Column::new_unqualified(name)),
1108 valid_fields: schema.columns().to_vec(),
1109 })
1110}
1111
1112pub fn add_possible_columns_to_diag(
1113 diagnostic: &mut Diagnostic,
1114 field: &Column,
1115 valid_fields: &[Column],
1116) {
1117 let field_names: Vec<String> = valid_fields
1118 .iter()
1119 .filter_map(|f| {
1120 if normalized_levenshtein(f.name(), field.name()) >= 0.5 {
1121 Some(f.flat_name())
1122 } else {
1123 None
1124 }
1125 })
1126 .collect();
1127
1128 for name in field_names {
1129 diagnostic.add_note(format!("possible column {name}"), None);
1130 }
1131}
1132
1133#[cfg(test)]
1134mod test {
1135 use super::*;
1136
1137 use std::mem::size_of;
1138 use std::sync::Arc;
1139
1140 use arrow::error::ArrowError;
1141 use insta::assert_snapshot;
1142
1143 fn ok_result() -> Result<()> {
1144 Ok(())
1145 }
1146
1147 #[test]
1148 fn test_assert_eq_or_internal_err_passes() -> Result<()> {
1149 assert_eq_or_internal_err!(1, 1);
1150 ok_result()
1151 }
1152
1153 #[test]
1154 fn test_assert_eq_or_internal_err_fails() {
1155 fn check() -> Result<()> {
1156 assert_eq_or_internal_err!(1, 2, "expected equality");
1157 ok_result()
1158 }
1159
1160 let err = check().unwrap_err();
1161 assert_snapshot!(
1162 err.to_string(),
1163 @r"
1164 Internal error: Assertion failed: 1 == 2 (left: 1, right: 2): expected equality.
1165 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1166 "
1167 );
1168 }
1169
1170 #[test]
1171 fn test_assert_ne_or_internal_err_passes() -> Result<()> {
1172 assert_ne_or_internal_err!(1, 2);
1173 ok_result()
1174 }
1175
1176 #[test]
1177 fn test_assert_ne_or_internal_err_fails() {
1178 fn check() -> Result<()> {
1179 assert_ne_or_internal_err!(3, 3, "values must differ");
1180 ok_result()
1181 }
1182
1183 let err = check().unwrap_err();
1184 assert_snapshot!(
1185 err.to_string(),
1186 @r"
1187 Internal error: Assertion failed: 3 != 3 (left: 3, right: 3): values must differ.
1188 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1189 "
1190 );
1191 }
1192
1193 #[test]
1194 fn test_assert_or_internal_err_passes() -> Result<()> {
1195 assert_or_internal_err!(true);
1196 assert_or_internal_err!(true, "message");
1197 ok_result()
1198 }
1199
1200 #[test]
1201 fn test_assert_or_internal_err_fails_default() {
1202 fn check() -> Result<()> {
1203 assert_or_internal_err!(false);
1204 ok_result()
1205 }
1206
1207 let err = check().unwrap_err();
1208 assert_snapshot!(
1209 err.to_string(),
1210 @r"
1211 Internal error: Assertion failed: false.
1212 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1213 "
1214 );
1215 }
1216
1217 #[test]
1218 fn test_assert_or_internal_err_fails_with_message() {
1219 fn check() -> Result<()> {
1220 assert_or_internal_err!(false, "custom message");
1221 ok_result()
1222 }
1223
1224 let err = check().unwrap_err();
1225 assert_snapshot!(
1226 err.to_string(),
1227 @r"
1228 Internal error: Assertion failed: false: custom message.
1229 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1230 "
1231 );
1232 }
1233
1234 #[test]
1235 fn test_assert_or_internal_err_with_format_arguments() {
1236 fn check() -> Result<()> {
1237 assert_or_internal_err!(false, "custom {}", 42);
1238 ok_result()
1239 }
1240
1241 let err = check().unwrap_err();
1242 assert_snapshot!(
1243 err.to_string(),
1244 @r"
1245 Internal error: Assertion failed: false: custom 42.
1246 This issue was likely caused by a bug in DataFusion's code. Please help us to resolve this by filing a bug report in our issue tracker: https://github.com/apache/datafusion/issues
1247 "
1248 );
1249 }
1250
1251 #[test]
1252 fn test_error_size() {
1253 assert_eq!(size_of::<SchemaError>(), 40);
1256 assert_eq!(size_of::<DataFusionError>(), 40);
1257 }
1258
1259 #[test]
1260 fn datafusion_error_to_arrow() {
1261 let res = return_arrow_error().unwrap_err();
1262 assert!(
1263 res.to_string()
1264 .starts_with("External error: Error during planning: foo")
1265 );
1266 }
1267
1268 #[test]
1269 fn arrow_error_to_datafusion() {
1270 let res = return_datafusion_error().unwrap_err();
1271 assert_eq!(res.strip_backtrace(), "Arrow error: Schema error: bar");
1272 }
1273
1274 #[cfg(feature = "backtrace")]
1276 #[test]
1277 fn test_enabled_backtrace() {
1278 match std::env::var("RUST_BACKTRACE") {
1279 Ok(val) if val == "1" => {}
1280 _ => panic!("Environment variable RUST_BACKTRACE must be set to 1"),
1281 };
1282
1283 let res: Result<(), DataFusionError> = plan_err!("Err");
1284 let err = res.unwrap_err().to_string();
1285 assert!(err.contains(DataFusionError::BACK_TRACE_SEP));
1286 assert_eq!(
1287 err.split(DataFusionError::BACK_TRACE_SEP)
1288 .collect::<Vec<&str>>()
1289 .first()
1290 .unwrap(),
1291 &"Error during planning: Err"
1292 );
1293 assert!(
1294 !err.split(DataFusionError::BACK_TRACE_SEP)
1295 .collect::<Vec<&str>>()
1296 .get(1)
1297 .unwrap()
1298 .is_empty()
1299 );
1300 }
1301
1302 #[cfg(not(feature = "backtrace"))]
1303 #[test]
1304 fn test_disabled_backtrace() {
1305 let res: Result<(), DataFusionError> = plan_err!("Err");
1306 let res = res.unwrap_err().to_string();
1307 assert!(!res.contains(DataFusionError::BACK_TRACE_SEP));
1308 assert_eq!(res, "Error during planning: Err");
1309 }
1310
1311 #[test]
1312 fn test_find_root_error() {
1313 do_root_test(
1314 DataFusionError::Context(
1315 "it happened!".to_string(),
1316 Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1317 ),
1318 DataFusionError::ResourcesExhausted("foo".to_string()),
1319 );
1320
1321 do_root_test(
1322 DataFusionError::ArrowError(
1323 Box::new(ArrowError::ExternalError(Box::new(
1324 DataFusionError::ResourcesExhausted("foo".to_string()),
1325 ))),
1326 None,
1327 ),
1328 DataFusionError::ResourcesExhausted("foo".to_string()),
1329 );
1330
1331 do_root_test(
1332 DataFusionError::External(Box::new(DataFusionError::ResourcesExhausted(
1333 "foo".to_string(),
1334 ))),
1335 DataFusionError::ResourcesExhausted("foo".to_string()),
1336 );
1337
1338 do_root_test(
1339 DataFusionError::External(Box::new(ArrowError::ExternalError(Box::new(
1340 DataFusionError::ResourcesExhausted("foo".to_string()),
1341 )))),
1342 DataFusionError::ResourcesExhausted("foo".to_string()),
1343 );
1344
1345 do_root_test(
1346 DataFusionError::ArrowError(
1347 Box::new(ArrowError::ExternalError(Box::new(
1348 ArrowError::ExternalError(Box::new(
1349 DataFusionError::ResourcesExhausted("foo".to_string()),
1350 )),
1351 ))),
1352 None,
1353 ),
1354 DataFusionError::ResourcesExhausted("foo".to_string()),
1355 );
1356
1357 do_root_test(
1358 DataFusionError::External(Box::new(Arc::new(
1359 DataFusionError::ResourcesExhausted("foo".to_string()),
1360 ))),
1361 DataFusionError::ResourcesExhausted("foo".to_string()),
1362 );
1363
1364 do_root_test(
1365 DataFusionError::External(Box::new(Arc::new(ArrowError::ExternalError(
1366 Box::new(DataFusionError::ResourcesExhausted("foo".to_string())),
1367 )))),
1368 DataFusionError::ResourcesExhausted("foo".to_string()),
1369 );
1370 }
1371
1372 #[test]
1373 fn test_make_error_parse_input() {
1374 let res: Result<(), DataFusionError> = plan_err!("Err");
1375 let res = res.unwrap_err();
1376 assert_eq!(res.strip_backtrace(), "Error during planning: Err");
1377
1378 let extra1 = "extra1";
1379 let extra2 = "extra2";
1380
1381 let res: Result<(), DataFusionError> = plan_err!("Err {} {}", extra1, extra2);
1382 let res = res.unwrap_err();
1383 assert_eq!(
1384 res.strip_backtrace(),
1385 "Error during planning: Err extra1 extra2"
1386 );
1387
1388 let res: Result<(), DataFusionError> =
1389 plan_err!("Err {:?} {:#?}", extra1, extra2);
1390 let res = res.unwrap_err();
1391 assert_eq!(
1392 res.strip_backtrace(),
1393 "Error during planning: Err \"extra1\" \"extra2\""
1394 );
1395
1396 let res: Result<(), DataFusionError> = plan_err!("Err {extra1} {extra2}");
1397 let res = res.unwrap_err();
1398 assert_eq!(
1399 res.strip_backtrace(),
1400 "Error during planning: Err extra1 extra2"
1401 );
1402
1403 let res: Result<(), DataFusionError> = plan_err!("Err {extra1:?} {extra2:#?}");
1404 let res = res.unwrap_err();
1405 assert_eq!(
1406 res.strip_backtrace(),
1407 "Error during planning: Err \"extra1\" \"extra2\""
1408 );
1409 }
1410
1411 #[test]
1412 fn external_error() {
1413 let generic_error: GenericError =
1415 Box::new(DataFusionError::Plan("test".to_string()));
1416 let datafusion_error: DataFusionError = generic_error.into();
1417 println!("{}", datafusion_error.strip_backtrace());
1418 assert_eq!(
1419 datafusion_error.strip_backtrace(),
1420 "Error during planning: test"
1421 );
1422
1423 let generic_error: GenericError = Box::new(io::Error::other("io error"));
1425 let datafusion_error: DataFusionError = generic_error.into();
1426 println!("{}", datafusion_error.strip_backtrace());
1427 assert_eq!(
1428 datafusion_error.strip_backtrace(),
1429 "External error: io error"
1430 );
1431 }
1432
1433 #[test]
1434 fn external_error_no_recursive() {
1435 let generic_error_1: GenericError = Box::new(io::Error::other("io error"));
1436 let external_error_1: DataFusionError = generic_error_1.into();
1437 let generic_error_2: GenericError = Box::new(external_error_1);
1438 let external_error_2: DataFusionError = generic_error_2.into();
1439
1440 println!("{external_error_2}");
1441 assert!(
1442 external_error_2
1443 .to_string()
1444 .starts_with("External error: io error")
1445 );
1446 }
1447
1448 fn return_arrow_error() -> arrow::error::Result<()> {
1451 Err(DataFusionError::Plan("foo".to_string()).into())
1453 }
1454
1455 fn return_datafusion_error() -> Result<()> {
1458 Err(ArrowError::SchemaError("bar".to_string()).into())
1460 }
1461
1462 fn do_root_test(e: DataFusionError, exp: DataFusionError) {
1463 let e = e.find_root();
1464
1465 assert_eq!(e.strip_backtrace(), exp.strip_backtrace());
1467 assert_eq!(std::mem::discriminant(e), std::mem::discriminant(&exp),)
1468 }
1469
1470 #[test]
1471 fn test_iter() {
1472 let err = DataFusionError::Collection(vec![
1473 DataFusionError::Plan("a".to_string()),
1474 DataFusionError::Collection(vec![
1475 DataFusionError::Plan("b".to_string()),
1476 DataFusionError::Plan("c".to_string()),
1477 ]),
1478 ]);
1479 let errs = err.iter().collect::<Vec<_>>();
1480 assert_eq!(errs.len(), 3);
1481 assert_eq!(errs[0].strip_backtrace(), "Error during planning: a");
1482 assert_eq!(errs[1].strip_backtrace(), "Error during planning: b");
1483 assert_eq!(errs[2].strip_backtrace(), "Error during planning: c");
1484 }
1485}