Skip to main content

parquet/
errors.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5//! Common Parquet errors and macros.
6
7use core::num::TryFromIntError;
8use std::error::Error;
9use std::string::FromUtf8Error;
10use std::{cell, io, result, str};
11
12#[cfg(feature = "arrow")]
13use arrow_schema::ArrowError;
14
15/// Parquet error enumeration
16// Note: we don't implement PartialEq as the semantics for the
17// external variant are not well defined (#4469)
18#[derive(Debug)]
19#[non_exhaustive]
20pub enum ParquetError {
21    /// General Parquet error.
22    /// Returned when code violates normal workflow of working with Parquet files.
23    General(String),
24    /// "Not yet implemented" Parquet error.
25    /// Returned when functionality is not yet available.
26    NYI(String),
27    /// "End of file" Parquet error.
28    /// Returned when IO related failures occur, e.g. when there are not enough bytes to
29    /// decode.
30    EOF(String),
31    #[cfg(feature = "arrow")]
32    /// Arrow error.
33    /// Returned when reading into arrow or writing from arrow.
34    ArrowError(String),
35    /// Error when the requested column index is more than the
36    /// number of columns in the row group
37    IndexOutOfBound(usize, usize),
38    /// An external error variant
39    External(Box<dyn Error + Send + Sync>),
40    /// Returned when a function needs more data to complete properly. The `usize` field indicates
41    /// the total number of bytes required, not the number of additional bytes.
42    NeedMoreData(usize),
43    /// Returned when a function needs more data to complete properly.
44    /// The `Range<u64>` indicates the range of bytes that are needed.
45    NeedMoreDataRange(std::ops::Range<u64>),
46}
47
48impl std::fmt::Display for ParquetError {
49    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
50        match &self {
51            ParquetError::General(message) => {
52                write!(fmt, "Parquet error: {message}")
53            }
54            ParquetError::NYI(message) => write!(fmt, "NYI: {message}"),
55            ParquetError::EOF(message) => write!(fmt, "EOF: {message}"),
56            #[cfg(feature = "arrow")]
57            ParquetError::ArrowError(message) => write!(fmt, "Arrow: {message}"),
58            ParquetError::IndexOutOfBound(index, bound) => {
59                write!(fmt, "Index {index} out of bound: {bound}")
60            }
61            ParquetError::External(e) => write!(fmt, "External: {e}"),
62            ParquetError::NeedMoreData(needed) => write!(fmt, "NeedMoreData: {needed}"),
63            ParquetError::NeedMoreDataRange(range) => {
64                write!(fmt, "NeedMoreDataRange: {}..{}", range.start, range.end)
65            }
66        }
67    }
68}
69
70impl Error for ParquetError {
71    fn source(&self) -> Option<&(dyn Error + 'static)> {
72        match self {
73            ParquetError::External(e) => Some(e.as_ref()),
74            _ => None,
75        }
76    }
77}
78
79impl From<TryFromIntError> for ParquetError {
80    fn from(e: TryFromIntError) -> ParquetError {
81        ParquetError::General(format!("Integer overflow: {e}"))
82    }
83}
84
85impl From<io::Error> for ParquetError {
86    fn from(e: io::Error) -> ParquetError {
87        ParquetError::External(Box::new(e))
88    }
89}
90
91#[cfg(any(feature = "snap", test))]
92impl From<snap::Error> for ParquetError {
93    fn from(e: snap::Error) -> ParquetError {
94        ParquetError::External(Box::new(e))
95    }
96}
97
98impl From<cell::BorrowMutError> for ParquetError {
99    fn from(e: cell::BorrowMutError) -> ParquetError {
100        ParquetError::External(Box::new(e))
101    }
102}
103
104impl From<str::Utf8Error> for ParquetError {
105    fn from(e: str::Utf8Error) -> ParquetError {
106        ParquetError::External(Box::new(e))
107    }
108}
109
110impl From<FromUtf8Error> for ParquetError {
111    fn from(e: FromUtf8Error) -> ParquetError {
112        ParquetError::External(Box::new(e))
113    }
114}
115
116#[cfg(feature = "arrow")]
117impl From<ArrowError> for ParquetError {
118    fn from(e: ArrowError) -> ParquetError {
119        ParquetError::External(Box::new(e))
120    }
121}
122
123#[cfg(feature = "object_store")]
124impl From<object_store::Error> for ParquetError {
125    fn from(e: object_store::Error) -> ParquetError {
126        ParquetError::External(Box::new(e))
127    }
128}
129
130#[cfg(feature = "encryption")]
131impl From<ring::error::Unspecified> for ParquetError {
132    fn from(e: ring::error::Unspecified) -> ParquetError {
133        ParquetError::External(Box::new(e))
134    }
135}
136
137/// A specialized `Result` for Parquet errors.
138pub type Result<T, E = ParquetError> = result::Result<T, E>;
139
140// ----------------------------------------------------------------------
141// Conversion from `ParquetError` to other types of `Error`s
142
143impl From<ParquetError> for io::Error {
144    fn from(e: ParquetError) -> Self {
145        io::Error::other(e)
146    }
147}
148
149// ----------------------------------------------------------------------
150// Convenient macros for different errors
151
152macro_rules! general_err {
153    ($fmt:literal) => (ParquetError::General(format!($fmt)));
154    ($fmt:expr) => (ParquetError::General($fmt.to_owned()));
155    ($fmt:expr, $($args:expr),*) => (ParquetError::General(format!($fmt, $($args),*)));
156    ($e:expr, $fmt:expr) => (ParquetError::General($fmt.to_owned(), $e));
157    ($e:ident, $fmt:expr, $($args:tt),*) => (
158        ParquetError::General(&format!($fmt, $($args),*), $e));
159}
160
161macro_rules! nyi_err {
162    ($fmt:literal) => (ParquetError::NYI(format!($fmt)));
163    ($fmt:expr) => (ParquetError::NYI($fmt.to_owned()));
164    ($fmt:expr, $($args:expr),*) => (ParquetError::NYI(format!($fmt, $($args),*)));
165}
166
167macro_rules! eof_err {
168    ($fmt:literal) => (ParquetError::EOF(format!($fmt)));
169    ($fmt:expr) => (ParquetError::EOF($fmt.to_owned()));
170    ($fmt:expr, $($args:expr),*) => (ParquetError::EOF(format!($fmt, $($args),*)));
171}
172
173#[cfg(feature = "arrow")]
174macro_rules! arrow_err {
175    ($fmt:literal) => (ParquetError::ArrowError(format!($fmt)));
176    ($fmt:expr) => (ParquetError::ArrowError($fmt.to_owned()));
177    ($fmt:expr, $($args:expr),*) => (ParquetError::ArrowError(format!($fmt, $($args),*)));
178    ($e:expr, $fmt:expr) => (ParquetError::ArrowError($fmt.to_owned(), $e));
179    ($e:ident, $fmt:expr, $($args:tt),*) => (
180        ParquetError::ArrowError(&format!($fmt, $($args),*), $e));
181}
182
183// ----------------------------------------------------------------------
184// Convert parquet error into other errors
185
186#[cfg(feature = "arrow")]
187impl From<ParquetError> for ArrowError {
188    fn from(p: ParquetError) -> Self {
189        Self::ParquetError(format!("{p}"))
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    /// The single-argument arms of the error macros must format their argument, so that
198    /// inline format arguments are not silently emitted as literal text.
199    #[test]
200    fn error_macros_format_inline_args() {
201        let expected = 1;
202        let actual = 2;
203        assert_eq!(
204            general_err!("expected {expected}, got {actual}").to_string(),
205            "Parquet error: expected 1, got 2"
206        );
207        assert_eq!(
208            nyi_err!("not yet: {expected}").to_string(),
209            "NYI: not yet: 1"
210        );
211        assert_eq!(eof_err!("eof at {actual}").to_string(), "EOF: eof at 2");
212    }
213}