use crate::to_rust_str;
use crate::AssertSend;
use crate::AssertSync;
use crate::Context;
#[cfg(doc)]
use crate::{Batch, BatchBuilder, Connection, Statement};
use odpic_sys::*;
use std::borrow::Cow;
use std::error;
use std::ffi::CStr;
use std::fmt;
#[cfg(feature = "struct_error")]
use std::fmt::Display;
use std::mem::MaybeUninit;
use std::num;
use std::str;
use std::sync;
pub(crate) const DPI_ERR_NOT_CONNECTED: i32 = 1010;
pub(crate) const DPI_ERR_BUFFER_SIZE_TOO_SMALL: i32 = 1019;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum ErrorKind {
OciError,
DpiError,
NullValue,
ParseError,
OutOfRange,
InvalidArgument,
InvalidTypeConversion,
InvalidBindIndex,
InvalidBindName,
InvalidColumnIndex,
InvalidColumnName,
InvalidAttributeName,
InvalidOperation,
UninitializedBindValue,
NoDataFound,
BatchErrors,
InternalError,
Other,
}
#[derive(Debug)]
#[cfg(feature = "struct_error")]
pub struct Error {
kind: ErrorKind,
message: Cow<'static, str>,
dberr: Option<Box<DbError>>,
batch_errors: Option<Vec<DbError>>,
source: Option<Box<dyn error::Error + Send + Sync>>,
}
#[non_exhaustive]
#[derive(Debug)]
#[cfg(not(feature = "struct_error"))]
pub enum Error {
#[deprecated(note = "Use kind() to check the error category. Use db_error() to get DbError.")]
OciError(DbError),
#[deprecated(note = "Use kind() to check the error category. Use db_error() to get DbError.")]
DpiError(DbError),
#[deprecated(note = "Use kind() to check the error category.")]
NullValue,
#[deprecated(
note = "Use kind() to check the error category. Use source() to get the underlying error."
)]
ParseError(Box<dyn error::Error + Send + Sync>),
#[deprecated(
note = "Use kind() to check the error category. Use to_string() to get the message."
)]
OutOfRange(String),
#[deprecated(note = "Use kind() to check the error category.")]
InvalidTypeConversion(String, String),
#[deprecated]
InvalidArgument {
message: Cow<'static, str>,
source: Option<Box<dyn error::Error + Send + Sync>>,
},
#[deprecated(note = "Use kind() to check the error category.")]
InvalidBindIndex(usize),
#[deprecated(note = "Use kind() to check the error category.")]
InvalidBindName(String),
#[deprecated(note = "Use kind() to check the error category.")]
InvalidColumnIndex(usize),
#[deprecated(note = "Use kind() to check the error category.")]
InvalidColumnName(String),
#[deprecated(note = "Use kind() to check the error category.")]
InvalidAttributeName(String),
#[deprecated(
note = "Use kind() to check the error category. Use to_string() to get the message."
)]
InvalidOperation(String),
#[deprecated(note = "Use kind() to check the error category.")]
UninitializedBindValue,
#[deprecated(note = "Use kind() to check the error category.")]
NoDataFound,
#[deprecated(
note = "Use kind() to check the error category. Use batch_errors() to get the db errors."
)]
BatchErrors(Vec<DbError>),
#[deprecated(
note = "Use kind() to check the error category. Use to_string() to get the message."
)]
InternalError(String),
#[non_exhaustive]
Other {
kind: ErrorKind,
message: Cow<'static, str>,
source: Option<Box<dyn error::Error + Send + Sync>>,
},
}
impl Error {
pub(crate) fn from_context(ctxt: &Context) -> Error {
let err = unsafe {
let mut err = MaybeUninit::uninit();
dpiContext_getError(ctxt.context, err.as_mut_ptr());
err.assume_init()
};
Error::from_dpi_error(&err)
}
pub(crate) fn from_dpi_error(err: &dpiErrorInfo) -> Error {
Error::from_db_error(DbError::from_dpi_error(err))
}
}
#[cfg(feature = "struct_error")]
impl Error {
pub fn new<M>(kind: ErrorKind, message: M) -> Error
where
M: Into<Cow<'static, str>>,
{
Error {
kind,
message: message.into(),
dberr: None,
batch_errors: None,
source: None,
}
}
pub(crate) fn add_dberr(self, dberr: DbError) -> Error {
Error {
dberr: Some(Box::new(dberr)),
..self
}
}
pub(crate) fn add_batch_errors(self, batch_errors: Vec<DbError>) -> Error {
Error {
batch_errors: Some(batch_errors),
..self
}
}
pub fn add_source<E>(self, source: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error {
source: Some(source.into()),
..self
}
}
pub fn with_source<E>(kind: ErrorKind, source: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error::new(kind, "").add_source(source)
}
pub(crate) fn from_db_error(dberr: DbError) -> Error {
let (kind, message_prefix) = if dberr.message().starts_with("DPI") {
(ErrorKind::DpiError, "DPI")
} else {
(ErrorKind::OciError, "OCI")
};
Error::new(kind, format!("{} Error: {}", message_prefix, dberr.message)).add_dberr(dberr)
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
pub fn db_error(&self) -> Option<&DbError> {
self.dberr.as_ref().map(|b| b.as_ref())
}
pub fn batch_errors(&self) -> Option<&Vec<DbError>> {
self.batch_errors.as_ref()
}
pub fn oci_code(&self) -> Option<i32> {
match (self.kind, &self.dberr) {
(ErrorKind::OciError, Some(dberr)) if dberr.code != 0 => Some(dberr.code),
_ => None,
}
}
pub fn dpi_code(&self) -> Option<i32> {
match (self.kind, &self.dberr) {
(ErrorKind::DpiError, Some(dberr)) => dpi_error_in_message(&dberr.message),
_ => None,
}
}
pub fn into_source(self) -> Option<Box<dyn error::Error + Send + Sync>> {
self.source
}
pub(crate) fn oci_error(dberr: DbError) -> Error {
Error::new(ErrorKind::OciError, format!("OCI Error: {}", dberr.message)).add_dberr(dberr)
}
pub(crate) fn null_value() -> Error {
Error::new(ErrorKind::NullValue, "NULL value found")
}
pub(crate) fn parse_error<T>(source: T) -> Error
where
T: Into<Box<dyn error::Error + Send + Sync>>,
{
let source = source.into();
Error::new(ErrorKind::ParseError, "").add_source(source)
}
pub(crate) fn out_of_range<T>(message: T) -> Error
where
T: Into<Cow<'static, str>>,
{
Error::new(ErrorKind::OutOfRange, message.into())
}
pub(crate) fn invalid_type_conversion<T1, T2>(from: T1, to: T2) -> Error
where
T1: Display,
T2: Display,
{
Error::new(
ErrorKind::InvalidTypeConversion,
format!("invalid type conversion from {} to {}", from, to),
)
}
pub(crate) fn invalid_bind_index<T>(index: T) -> Error
where
T: Display,
{
Error::new(
ErrorKind::InvalidBindIndex,
format!("invalid bind index {} (one-based)", index),
)
}
pub(crate) fn invalid_bind_name<T>(name: T) -> Error
where
T: Display,
{
Error::new(
ErrorKind::InvalidBindName,
format!("invalid bind name {}", name),
)
}
pub(crate) fn invalid_column_index<T>(index: T) -> Error
where
T: Display,
{
Error::new(
ErrorKind::InvalidColumnIndex,
format!("invalid column index {} (zero-based)", index),
)
}
pub(crate) fn invalid_column_name<T>(name: T) -> Error
where
T: Display,
{
Error::new(
ErrorKind::InvalidColumnName,
format!("invalid column name {}", name),
)
}
pub(crate) fn invalid_attribute_name<T>(name: T) -> Error
where
T: Display,
{
Error::new(
ErrorKind::InvalidAttributeName,
format!("invalid attribute name {}", name),
)
}
pub(crate) fn invalid_operation<T>(message: T) -> Error
where
T: Into<Cow<'static, str>>,
{
Error::new(ErrorKind::InvalidOperation, message.into())
}
pub(crate) fn uninitialized_bind_value() -> Error {
Error::new(
ErrorKind::UninitializedBindValue,
"try to access uninitialized bind value",
)
}
pub(crate) fn no_data_found() -> Error {
Error::new(ErrorKind::NoDataFound, "no data found")
}
pub(crate) fn make_batch_errors(batch_errors: Vec<DbError>) -> Error {
Error::new(
ErrorKind::BatchErrors,
format!("batch error containing {} error(s)", batch_errors.len()),
)
.add_batch_errors(batch_errors)
}
pub(crate) fn internal_error<T>(message: T) -> Error
where
T: Into<Cow<'static, str>>,
{
Error::new(ErrorKind::InternalError, message.into())
}
pub(crate) fn invalid_argument<M>(message: M) -> Error
where
M: Into<Cow<'static, str>>,
{
Error::new(ErrorKind::InvalidArgument, message.into())
}
}
#[allow(deprecated)]
#[cfg(not(feature = "struct_error"))]
impl Error {
pub fn new<M>(kind: ErrorKind, message: M) -> Error
where
M: Into<Cow<'static, str>>,
{
Error::Other {
kind,
message: message.into(),
source: None,
}
}
pub(crate) fn from_db_error(dberr: DbError) -> Error {
if dberr.message().starts_with("DPI") {
Error::DpiError(dberr)
} else {
Error::OciError(dberr)
}
}
pub fn add_source<E>(self, source: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
match self {
Error::InvalidArgument { message, .. } => Error::InvalidArgument {
message,
source: Some(source.into()),
},
Error::Other { kind, message, .. } => Error::Other {
kind,
message,
source: Some(source.into()),
},
_ => self,
}
}
pub fn with_source<E>(kind: ErrorKind, source: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Error::new(kind, "").add_source(source)
}
pub fn kind(&self) -> ErrorKind {
match self {
Error::OciError(_) => ErrorKind::OciError,
Error::DpiError(_) => ErrorKind::DpiError,
Error::NullValue => ErrorKind::NullValue,
Error::ParseError(_) => ErrorKind::ParseError,
Error::OutOfRange(_) => ErrorKind::OutOfRange,
Error::InvalidArgument { .. } => ErrorKind::InvalidArgument,
Error::InvalidTypeConversion(_, _) => ErrorKind::InvalidTypeConversion,
Error::InvalidBindIndex(_) => ErrorKind::InvalidBindIndex,
Error::InvalidBindName(_) => ErrorKind::InvalidBindName,
Error::InvalidColumnIndex(_) => ErrorKind::InvalidColumnIndex,
Error::InvalidColumnName(_) => ErrorKind::InvalidColumnName,
Error::InvalidAttributeName(_) => ErrorKind::InvalidAttributeName,
Error::InvalidOperation(_) => ErrorKind::InvalidOperation,
Error::UninitializedBindValue => ErrorKind::UninitializedBindValue,
Error::NoDataFound => ErrorKind::NoDataFound,
Error::BatchErrors(_) => ErrorKind::BatchErrors,
Error::InternalError(_) => ErrorKind::InternalError,
Error::Other { kind, .. } => *kind,
}
}
pub fn db_error(&self) -> Option<&DbError> {
match self {
Error::OciError(err) | Error::DpiError(err) => Some(err),
_ => None,
}
}
pub fn batch_errors(&self) -> Option<&Vec<DbError>> {
match self {
Error::BatchErrors(errs) => Some(errs),
_ => None,
}
}
pub fn oci_code(&self) -> Option<i32> {
if let Error::OciError(dberr) = &self {
if dberr.code != 0 {
Some(dberr.code)
} else {
None
}
} else {
None
}
}
pub fn dpi_code(&self) -> Option<i32> {
if let Error::DpiError(dberr) = &self {
dpi_error_in_message(&dberr.message)
} else {
None
}
}
pub fn into_source(self) -> Option<Box<dyn error::Error + Send + Sync>> {
match self {
Error::ParseError(err) => Some(err),
Error::InvalidArgument { source, .. } => source,
Error::Other { source, .. } => source,
_ => None,
}
}
pub(crate) fn oci_error(dberr: DbError) -> Error {
Error::OciError(dberr)
}
pub(crate) fn null_value() -> Error {
Error::NullValue
}
pub(crate) fn parse_error<T>(source: T) -> Error
where
T: Into<Box<dyn error::Error + Send + Sync>>,
{
Error::ParseError(source.into())
}
pub(crate) fn out_of_range<T>(message: T) -> Error
where
T: Into<String>,
{
Error::OutOfRange(message.into())
}
pub(crate) fn invalid_type_conversion<T1, T2>(from: T1, to: T2) -> Error
where
T1: Into<String>,
T2: Into<String>,
{
Error::InvalidTypeConversion(from.into(), to.into())
}
pub(crate) fn invalid_bind_index(index: usize) -> Error {
Error::InvalidBindIndex(index)
}
pub(crate) fn invalid_bind_name<T>(name: T) -> Error
where
T: Into<String>,
{
Error::InvalidBindName(name.into())
}
pub(crate) fn invalid_column_index(index: usize) -> Error {
Error::InvalidColumnIndex(index)
}
pub(crate) fn invalid_column_name<T>(name: T) -> Error
where
T: Into<String>,
{
Error::InvalidColumnName(name.into())
}
pub(crate) fn invalid_attribute_name<T>(name: T) -> Error
where
T: Into<String>,
{
Error::InvalidAttributeName(name.into())
}
pub(crate) fn invalid_operation<T>(message: T) -> Error
where
T: Into<String>,
{
Error::InvalidOperation(message.into())
}
pub(crate) fn uninitialized_bind_value() -> Error {
Error::UninitializedBindValue
}
pub(crate) fn no_data_found() -> Error {
Error::NoDataFound
}
pub(crate) fn make_batch_errors(errs: Vec<DbError>) -> Error {
Error::BatchErrors(errs)
}
pub(crate) fn internal_error<T>(message: T) -> Error
where
T: Into<String>,
{
Error::InternalError(message.into())
}
pub(crate) fn invalid_argument<M>(message: M) -> Error
where
M: Into<Cow<'static, str>>,
{
Error::InvalidArgument {
message: message.into(),
source: None,
}
}
}
impl AssertSend for Error {}
impl AssertSync for Error {}
#[derive(Eq, PartialEq, Clone)]
pub struct ParseOracleTypeError {
typename: &'static str,
}
impl ParseOracleTypeError {
pub fn new(typename: &'static str) -> ParseOracleTypeError {
ParseOracleTypeError { typename }
}
}
impl fmt::Display for ParseOracleTypeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} parse error", self.typename)
}
}
impl fmt::Debug for ParseOracleTypeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ParseOracleTypeError")
}
}
impl error::Error for ParseOracleTypeError {
fn description(&self) -> &str {
"Oracle type parse error"
}
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct DbError {
code: i32,
offset: u32,
message: String,
fn_name: Cow<'static, str>,
action: Cow<'static, str>,
is_recoverable: bool,
is_warning: bool,
}
impl DbError {
pub(crate) fn from_dpi_error(err: &dpiErrorInfo) -> DbError {
DbError {
code: err.code,
offset: err.offset,
message: to_rust_str(err.message, err.messageLength),
fn_name: unsafe { CStr::from_ptr(err.fnName) }.to_string_lossy(),
action: unsafe { CStr::from_ptr(err.action) }.to_string_lossy(),
is_recoverable: err.isRecoverable != 0,
is_warning: err.isWarning != 0,
}
}
pub(crate) fn to_warning(ctxt: &Context) -> Option<DbError> {
let err = unsafe {
let mut err = MaybeUninit::uninit();
dpiContext_getError(ctxt.context, err.as_mut_ptr());
err.assume_init()
};
if err.isWarning != 0 {
Some(DbError::from_dpi_error(&err))
} else {
None
}
}
pub fn new<M, F, A>(code: i32, offset: u32, message: M, fn_name: F, action: A) -> DbError
where
M: Into<String>,
F: Into<Cow<'static, str>>,
A: Into<Cow<'static, str>>,
{
DbError {
code,
offset,
message: message.into(),
fn_name: fn_name.into(),
action: action.into(),
is_recoverable: false,
is_warning: false,
}
}
pub fn code(&self) -> i32 {
self.code
}
pub fn offset(&self) -> u32 {
self.offset
}
pub fn message(&self) -> &str {
&self.message
}
pub fn fn_name(&self) -> &str {
&self.fn_name
}
pub fn action(&self) -> &str {
&self.action
}
pub fn is_recoverable(&self) -> bool {
self.is_recoverable
}
pub fn is_warning(&self) -> bool {
self.is_warning
}
}
#[cfg(feature = "struct_error")]
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.message.is_empty() {
write!(f, "{}", self.message)
} else if let Some(source) = &self.source {
write!(f, "{}", source)
} else {
write!(f, "blank error message")
}
}
}
#[cfg(not(feature = "struct_error"))]
impl fmt::Display for Error {
#[allow(deprecated)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::OciError(err) => write!(f, "OCI Error: {}", err.message),
Error::DpiError(err) => write!(f, "DPI Error: {}", err.message),
Error::NullValue => write!(f, "NULL value found"),
Error::ParseError(err) => write!(f, "{}", err),
Error::OutOfRange(msg) => write!(f, "{}", msg),
Error::InvalidTypeConversion(from, to) => {
write!(f, "invalid type conversion from {} to {}", from, to)
}
Error::InvalidBindIndex(idx) => {
write!(f, "invalid bind index {} (one-based)", idx)
}
Error::InvalidBindName(name) => write!(f, "invalid bind name {}", name),
Error::InvalidColumnIndex(idx) => {
write!(f, "invalid column index {} (zero-based)", idx)
}
Error::InvalidColumnName(name) => write!(f, "invalid column name {}", name),
Error::InvalidAttributeName(name) => write!(f, "invalid attribute name {}", name),
Error::InvalidOperation(msg) => write!(f, "{}", msg),
Error::UninitializedBindValue => write!(f, "try to access uninitialized bind value"),
Error::NoDataFound => write!(f, "no data found"),
Error::BatchErrors(errs) => {
write!(f, "batch errors (")?;
for err in errs {
write!(f, "{}, ", err)?;
}
write!(f, ")")
}
Error::InternalError(msg) => write!(f, "{}", msg),
Error::InvalidArgument { message, .. } => write!(f, "{}", message),
Error::Other {
message, source, ..
} => {
if !message.is_empty() {
write!(f, "{}", message)
} else if let Some(source) = source {
write!(f, "{}", source)
} else {
write!(f, "blank error message")
}
}
}
}
}
#[cfg(feature = "struct_error")]
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
if let Some(ref err) = self.source {
Some(err.as_ref())
} else {
None
}
}
}
#[cfg(not(feature = "struct_error"))]
impl error::Error for Error {
#[allow(deprecated)]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::ParseError(err) => Some(err.as_ref()),
Error::InvalidArgument {
source: Some(source),
..
} => Some(source.as_ref()),
Error::Other {
source: Some(source),
..
} => Some(source.as_ref()),
_ => None,
}
}
}
impl fmt::Display for DbError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl From<ParseOracleTypeError> for Error {
fn from(err: ParseOracleTypeError) -> Self {
Error::parse_error(err)
}
}
impl From<num::ParseIntError> for Error {
fn from(err: num::ParseIntError) -> Self {
Error::parse_error(err)
}
}
impl From<num::ParseFloatError> for Error {
fn from(err: num::ParseFloatError) -> Self {
Error::parse_error(err)
}
}
impl From<num::TryFromIntError> for Error {
fn from(err: num::TryFromIntError) -> Self {
Error::parse_error(err)
}
}
impl From<str::Utf8Error> for Error {
fn from(err: str::Utf8Error) -> Self {
Error::parse_error(err)
}
}
impl<T> From<sync::PoisonError<T>> for Error {
fn from(err: sync::PoisonError<T>) -> Self {
Error::internal_error(err.to_string())
}
}
fn dpi_error_in_message(message: &str) -> Option<i32> {
let bytes = message.as_bytes();
if !bytes.starts_with(b"DPI-") {
return None;
}
let mut code = 0;
for c in bytes.iter().skip(4) {
if b'0' <= *c && *c <= b'9' {
code *= 10;
code += (*c - b'0') as i32;
} else if *c == b':' {
return Some(code);
} else {
break;
}
}
None
}
#[macro_export]
#[doc(hidden)]
macro_rules! chkerr {
($ctxt:expr, $code:expr) => {{
#[allow(unused_unsafe)]
if unsafe { $code } != DPI_SUCCESS as i32 {
return Err($crate::Error::from_context($ctxt));
}
}};
($ctxt:expr, $code:expr, $cleanup:stmt) => {{
#[allow(unused_unsafe)]
if unsafe { $code } != DPI_SUCCESS as i32 {
let err = $crate::Error::from_context($ctxt);
$cleanup
return Err(err);
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as StdError;
use std::io;
#[test]
fn test_dpi_error_in_message() {
assert_eq!(None, dpi_error_in_message("ORA-1234"));
assert_eq!(None, dpi_error_in_message("DPI-1234"));
assert_eq!(Some(1234), dpi_error_in_message("DPI-1234: xxx"));
}
#[test]
fn new_and_add_source() {
let err = Error::new(ErrorKind::Other, "custom error");
assert_eq!(err.kind(), ErrorKind::Other);
assert_eq!(err.to_string(), "custom error");
assert!(err.source().is_none());
let err = err.add_source(io::Error::new(io::ErrorKind::Other, "io error"));
assert_eq!(err.kind(), ErrorKind::Other);
assert_eq!(err.to_string(), "custom error");
assert!(err.source().is_some());
}
#[test]
fn with_source() {
let err = Error::with_source(
ErrorKind::InvalidTypeConversion,
io::Error::new(io::ErrorKind::Other, "io error"),
);
assert_eq!(err.kind(), ErrorKind::InvalidTypeConversion);
assert_eq!(err.to_string(), "io error");
assert!(err.source().is_some());
}
}