use crate::binding::dpiContext_getError;
use crate::binding::dpiErrorInfo;
use crate::to_rust_str;
use crate::AssertSend;
use crate::AssertSync;
use crate::Context;
#[cfg(doc)]
use crate::{Batch, BatchBuilder, Connection, Statement};
use std::error;
use std::ffi::CStr;
use std::fmt;
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,
InvalidTypeConversion,
InvalidBindIndex,
InvalidBindName,
InvalidColumnIndex,
InvalidColumnName,
InvalidAttributeName,
InvalidOperation,
UninitializedBindValue,
NoDataFound,
BatchErrors,
InternalError,
}
#[non_exhaustive]
#[derive(Debug)]
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(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),
}
#[allow(deprecated)]
impl Error {
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::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,
}
}
pub(crate) fn add_source<E>(self, _source: E) -> Error
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
self
}
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(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())
}
}
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: String,
action: String,
}
impl DbError {
pub fn new(
code: i32,
offset: u32,
message: String,
fn_name: String,
action: String,
) -> DbError {
DbError {
code,
offset,
message,
fn_name,
action,
}
}
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
}
}
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),
}
}
}
impl error::Error for Error {
#[allow(deprecated)]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::ParseError(err) => Some(err.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())
}
}
pub fn dberror_from_dpi_error(err: &dpiErrorInfo) -> DbError {
DbError::new(
err.code,
err.offset,
to_rust_str(err.message, err.messageLength),
unsafe { CStr::from_ptr(err.fnName) }
.to_string_lossy()
.into_owned(),
unsafe { CStr::from_ptr(err.action) }
.to_string_lossy()
.into_owned(),
)
}
#[allow(deprecated)]
pub fn error_from_dpi_error(err: &dpiErrorInfo) -> Error {
let err = dberror_from_dpi_error(err);
if err.message().starts_with("DPI") {
Error::DpiError(err)
} else {
Error::OciError(err)
}
}
pub(crate) fn 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(crate) fn error_from_context(ctxt: &Context) -> Error {
let err = unsafe {
let mut err = MaybeUninit::uninit();
dpiContext_getError(ctxt.context, err.as_mut_ptr());
err.assume_init()
};
crate::error::error_from_dpi_error(&err)
}
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::error_from_context($ctxt));
}
}};
($ctxt:expr, $code:expr, $cleanup:stmt) => {{
#[allow(unused_unsafe)]
if unsafe { $code } != DPI_SUCCESS as i32 {
let err = $crate::error::error_from_context($ctxt);
$cleanup
return Err(err);
}
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[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"));
}
}