use std::collections::HashMap;
use std::error;
use std::io::IoError;
use std::fmt;
use std::result;
use openssl::ssl::error::SslError;
use phf;
use Result;
use types::Type;
macro_rules! make_errors(
($($code:expr => $error:ident),+) => (
#[deriving(PartialEq, Eq, Clone, Show)]
#[allow(missing_docs)]
pub enum SqlState {
$($error,)+
Unknown(String)
}
static STATE_MAP: phf::Map<&'static str, SqlState> = phf_map!(
$($code => SqlState::$error),+
);
impl SqlState {
#[doc(hidden)]
pub fn from_code(s: &str) -> SqlState {
match STATE_MAP.get(s) {
Some(state) => state.clone(),
None => SqlState::Unknown(s.into_string())
}
}
}
)
)
make_errors!(
"00000" => SuccessfulCompletion,
"01000" => Warning,
"0100C" => DynamicResultSetsReturned,
"01008" => ImplicitZeroBitPadding,
"01003" => NullValueEliminatedInSetFunction,
"01007" => PrivilegeNotGranted,
"01006" => PrivilegeNotRevoked,
"01004" => StringDataRightTruncationWarning,
"01P01" => DeprecatedFeature,
"02000" => NoData,
"02001" => NoAdditionalDynamicResultSetsReturned,
"03000" => SqlStatementNotYetComplete,
"08000" => ConnectionException,
"08003" => ConnectionDoesNotExist,
"08006" => ConnectionFailure,
"08001" => SqlclientUnableToEstablishSqlconnection,
"08004" => SqlserverRejectedEstablishmentOfSqlconnection,
"08007" => TransactionResolutionUnknown,
"08P01" => ProtocolViolation,
"09000" => TriggeredActionException,
"0A000" => FeatureNotSupported,
"0B000" => InvalidTransactionInitiation,
"0F000" => LocatorException,
"0F001" => InvalidLocatorException,
"0L000" => InvalidGrantor,
"0LP01" => InvalidGrantOperation,
"0P000" => InvalidRoleSpecification,
"0Z000" => DiagnosticsException,
"0Z002" => StackedDiagnosticsAccessedWithoutActiveHandler,
"20000" => CaseNotFound,
"21000" => CardinalityViolation,
"22000" => DataException,
"2202E" => ArraySubscriptError,
"22021" => CharacterNotInRepertoire,
"22008" => DatetimeFieldOverflow,
"22012" => DivisionByZero,
"22005" => ErrorInAssignment,
"2200B" => EscapeCharacterConflict,
"22022" => IndicatorOverflow,
"22015" => IntervalFieldOverflow,
"2201E" => InvalidArgumentForLogarithm,
"22014" => InvalidArgumentForNtileFunction,
"22016" => InvalidArgumentForNthValueFunction,
"2201F" => InvalidArgumentForPowerFunction,
"2201G" => InvalidArgumentForWidthBucketFunction,
"22018" => InvalidCharacterValueForCast,
"22007" => InvalidDatetimeFormat,
"22019" => InvalidEscapeCharacter,
"2200D" => InvalidEscapeOctet,
"22025" => InvalidEscapeSequence,
"22P06" => NonstandardUseOfEscapeCharacter,
"22010" => InvalidIndicatorParameterValue,
"22023" => InvalidParameterValue,
"2201B" => InvalidRegularExpression,
"2201W" => InvalidRowCountInLimitClause,
"2201X" => InvalidRowCountInResultOffsetClause,
"22009" => InvalidTimeZoneDisplacementValue,
"2200C" => InvalidUseOfEscapeCharacter,
"2200G" => MostSpecificTypeMismatch,
"22004" => NullValueNotAllowedData,
"22002" => NullValueNoIndicatorParameter,
"22003" => NumericValueOutOfRange,
"22026" => StringDataLengthMismatch,
"22001" => StringDataRightTruncationException,
"22011" => SubstringError,
"22027" => TrimError,
"22024" => UnterminatedCString,
"2200F" => ZeroLengthCharacterString,
"22P01" => FloatingPointException,
"22P02" => InvalidTextRepresentation,
"22P03" => InvalidBinaryRepresentation,
"22P04" => BadCopyFileFormat,
"22P05" => UntranslatableCharacter,
"2200L" => NotAnXmlDocument,
"2200M" => InvalidXmlDocument,
"2200N" => InvalidXmlContent,
"2200S" => InvalidXmlComment,
"2200T" => InvalidXmlProcessingInstruction,
"23000" => IntegrityConstraintViolation,
"23001" => RestrictViolation,
"23502" => NotNullViolation,
"23503" => ForeignKeyViolation,
"23505" => UniqueViolation,
"23514" => CheckViolation,
"32P01" => ExclusionViolation,
"24000" => InvalidCursorState,
"25000" => InvalidTransactionState,
"25001" => ActiveSqlTransaction,
"25002" => BranchTransactionAlreadyActive,
"25008" => HeldCursorRequiresSameIsolationLevel,
"25003" => InappropriateAccessModeForBranchTransaction,
"25004" => InappropriateIsolationLevelForBranchTransaction,
"25005" => NoActiveSqlTransactionForBranchTransaction,
"25006" => ReadOnlySqlTransaction,
"25007" => SchemaAndDataStatementMixingNotSupported,
"25P01" => NoActiveSqlTransaction,
"25P02" => InFailedSqlTransaction,
"26000" => InvalidSqlStatementName,
"27000" => TriggeredDataChangeViolation,
"28000" => InvalidAuthorizationSpecification,
"28P01" => InvalidPassword,
"2B000" => DependentPrivilegeDescriptorsStillExist,
"2BP01" => DependentObjectsStillExist,
"2D000" => InvalidTransactionTermination,
"2F000" => SqlRoutineException,
"2F005" => FunctionExecutedNoReturnStatement,
"2F002" => ModifyingSqlDataNotPermittedSqlRoutine,
"2F003" => ProhibitedSqlStatementAttemptedSqlRoutine,
"2F004" => ReadingSqlDataNotPermittedSqlRoutine,
"34000" => InvalidCursorName,
"38000" => ExternalRoutineException,
"38001" => ContainingSqlNotPermitted,
"38002" => ModifyingSqlDataNotPermittedExternalRoutine,
"38003" => ProhibitedSqlStatementAttemptedExternalRoutine,
"38004" => ReadingSqlDataNotPermittedExternalRoutine,
"39000" => ExternalRoutineInvocationException,
"39001" => InvalidSqlstateReturned,
"39004" => NullValueNotAllowedExternalRoutine,
"39P01" => TriggerProtocolViolated,
"39P02" => SrfProtocolViolated,
"3B000" => SavepointException,
"3B001" => InvalidSavepointException,
"3D000" => InvalidCatalogName,
"3F000" => InvalidSchemaName,
"40000" => TransactionRollback,
"40002" => TransactionIntegrityConstraintViolation,
"40001" => SerializationFailure,
"40003" => StatementCompletionUnknown,
"40P01" => DeadlockDetected,
"42000" => SyntaxErrorOrAccessRuleViolation,
"42601" => SyntaxError,
"42501" => InsufficientPrivilege,
"42846" => CannotCoerce,
"42803" => GroupingError,
"42P20" => WindowingError,
"42P19" => InvalidRecursion,
"42830" => InvalidForeignKey,
"42602" => InvalidName,
"42622" => NameTooLong,
"42939" => ReservedName,
"42804" => DatatypeMismatch,
"42P18" => IndeterminateDatatype,
"42P21" => CollationMismatch,
"42P22" => IndeterminateCollation,
"42809" => WrongObjectType,
"42703" => UndefinedColumn,
"42883" => UndefinedFunction,
"42P01" => UndefinedTable,
"42P02" => UndefinedParameter,
"42704" => UndefinedObject,
"42701" => DuplicateColumn,
"42P03" => DuplicateCursor,
"42P04" => DuplicateDatabase,
"42723" => DuplicateFunction,
"42P05" => DuplicatePreparedStatement,
"42P06" => DuplicateSchema,
"42P07" => DuplicateTable,
"42712" => DuplicateAliaas,
"42710" => DuplicateObject,
"42702" => AmbiguousColumn,
"42725" => AmbiguousFunction,
"42P08" => AmbiguousParameter,
"42P09" => AmbiguousAlias,
"42P10" => InvalidColumnReference,
"42611" => InvalidColumnDefinition,
"42P11" => InvalidCursorDefinition,
"42P12" => InvalidDatabaseDefinition,
"42P13" => InvalidFunctionDefinition,
"42P14" => InvalidPreparedStatementDefinition,
"42P15" => InvalidSchemaDefinition,
"42P16" => InvalidTableDefinition,
"42P17" => InvalidObjectDefinition,
"44000" => WithCheckOptionViolation,
"53000" => InsufficientResources,
"53100" => DiskFull,
"53200" => OutOfMemory,
"53300" => TooManyConnections,
"53400" => ConfigurationLimitExceeded,
"54000" => ProgramLimitExceeded,
"54001" => StatementTooComplex,
"54011" => TooManyColumns,
"54023" => TooManyArguments,
"55000" => ObjectNotInPrerequisiteState,
"55006" => ObjectInUse,
"55P02" => CantChangeRuntimeParam,
"55P03" => LockNotAvailable,
"57000" => OperatorIntervention,
"57014" => QueryCanceled,
"57P01" => AdminShutdown,
"57P02" => CrashShutdown,
"57P03" => CannotConnectNow,
"57P04" => DatabaseDropped,
"58000" => SystemError,
"58030" => IoError,
"58P01" => UndefinedFile,
"58P02" => DuplicateFile,
"F0000" => ConfigFileError,
"F0001" => LockFileExists,
"HV000" => FdwError,
"HV005" => FdwColumnNameNotFound,
"HV002" => FdwDynamicParameterValueNeeded,
"HV010" => FdwFunctionSequenceError,
"HV021" => FdwInconsistentDescriptorInformation,
"HV024" => FdwInvalidAttributeValue,
"HV007" => FdwInvalidColumnName,
"HV008" => FdwInvalidColumnNumber,
"HV004" => FdwInvalidDataType,
"HV006" => FdwInvalidDataTypeDescriptors,
"HV091" => FdwInvalidDescriptorFieldIdentifier,
"HV00B" => FdwInvalidHandle,
"HV00C" => FdwInvalidOptionIndex,
"HV00D" => FdwInvalidOptionName,
"HV090" => FdwInvalidStringLengthOrBufferLength,
"HV00A" => FdwInvalidStringFormat,
"HV009" => FdwInvalidUseOfNullPointer,
"HV014" => FdwTooManyHandles,
"HV001" => FdwOutOfMemory,
"HV00P" => FdwNoSchemas,
"HV00J" => FdwOptionNameNotFound,
"HV00K" => FdwReplyHandle,
"HV00Q" => FdwSchemaNotFound,
"HV00R" => FdwTableNotFound,
"HV00L" => FdwUnableToCreateExcecution,
"HV00M" => FdwUnableToCreateReply,
"HV00N" => FdwUnableToEstablishConnection,
"P0000" => PlpgsqlError,
"P0001" => RaiseException,
"P0002" => NoDataFound,
"P0003" => TooManyRows,
"XX000" => InternalError,
"XX001" => DataCorrupted,
"XX002" => IndexCorrupted
)
#[deriving(Clone, PartialEq, Eq)]
pub enum ConnectError {
InvalidUrl(String),
MissingUser,
DbError(DbError),
MissingPassword,
UnsupportedAuthentication,
NoSslSupport,
SslError(SslError),
IoError(IoError),
BadResponse,
}
impl error::Error for ConnectError {
fn description(&self) -> &str {
match *self {
ConnectError::InvalidUrl(_) => "Invalid URL",
ConnectError::MissingUser => "User missing in URL",
ConnectError::DbError(_) => "An error from the Postgres server itself",
ConnectError::MissingPassword => "The server requested a password but none was provided",
ConnectError::UnsupportedAuthentication => {
"The server requested an unsupported authentication method"
}
ConnectError::NoSslSupport => "The server does not support SSL",
ConnectError::SslError(_) => "Error initiating SSL session",
ConnectError::IoError(_) => "Error communicating with server",
ConnectError::BadResponse => "The server returned an unexpected response",
}
}
fn detail(&self) -> Option<String> {
match *self {
ConnectError::InvalidUrl(ref msg) => Some(msg.clone()),
_ => None,
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
ConnectError::DbError(ref err) => Some(err as &error::Error),
ConnectError::SslError(ref err) => Some(err as &error::Error),
ConnectError::IoError(ref err) => Some(err as &error::Error),
_ => None
}
}
}
impl error::FromError<IoError> for ConnectError {
fn from_error(err: IoError) -> ConnectError {
ConnectError::IoError(err)
}
}
impl error::FromError<DbError> for ConnectError {
fn from_error(err: DbError) -> ConnectError {
ConnectError::DbError(err)
}
}
impl error::FromError<SslError> for ConnectError {
fn from_error(err: SslError) -> ConnectError {
ConnectError::SslError(err)
}
}
impl fmt::Show for ConnectError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConnectError::InvalidUrl(ref err) => write!(fmt, "Invalid URL: {}", err),
ConnectError::MissingUser => write!(fmt, "User missing in URL"),
ConnectError::DbError(ref err) => err.fmt(fmt),
ConnectError::MissingPassword =>
write!(fmt, "The server requested a password but none was provided"),
ConnectError::UnsupportedAuthentication =>
write!(fmt, "The server requested an unsupported authentication method"),
ConnectError::NoSslSupport =>
write!(fmt, "The server does not support SSL"),
ConnectError::SslError(ref err) =>
write!(fmt, "Error initiating SSL session: {}", err),
ConnectError::IoError(ref err) =>
write!(fmt, "Error communicating with server: {}", err),
ConnectError::BadResponse =>
write!(fmt, "The server returned an unexpected response"),
}
}
}
#[deriving(Clone, PartialEq, Eq)]
pub enum ErrorPosition {
Normal(uint),
Internal {
position: uint,
query: String
}
}
#[deriving(Clone, PartialEq, Eq)]
pub struct DbError {
pub severity: String,
pub code: SqlState,
pub message: String,
pub detail: Option<String>,
pub hint: Option<String>,
pub position: Option<ErrorPosition>,
pub where_: Option<String>,
pub schema: Option<String>,
pub table: Option<String>,
pub column: Option<String>,
pub datatype: Option<String>,
pub constraint: Option<String>,
pub file: String,
pub line: uint,
pub routine: String
}
impl DbError {
#[doc(hidden)]
pub fn new_raw(fields: Vec<(u8, String)>) -> result::Result<DbError, ()> {
let mut map: HashMap<_, _> = fields.into_iter().collect();
Ok(DbError {
severity: try!(map.remove(&b'S').ok_or(())),
code: SqlState::from_code(try!(map.remove(&b'C').ok_or(()))[]),
message: try!(map.remove(&b'M').ok_or(())),
detail: map.remove(&b'D'),
hint: map.remove(&b'H'),
position: match map.remove(&b'P') {
Some(pos) => Some(ErrorPosition::Normal(try!(from_str(pos[]).ok_or(())))),
None => match map.remove(&b'p') {
Some(pos) => Some(ErrorPosition::Internal {
position: try!(from_str(pos[]).ok_or(())),
query: try!(map.remove(&b'q').ok_or(()))
}),
None => None
}
},
where_: map.remove(&b'W'),
schema: map.remove(&b's'),
table: map.remove(&b't'),
column: map.remove(&b'c'),
datatype: map.remove(&b'd'),
constraint: map.remove(&b'n'),
file: try!(map.remove(&b'F').ok_or(())),
line: try!(map.remove(&b'L').and_then(|l| from_str(l[])).ok_or(())),
routine: try!(map.remove(&b'R').ok_or(())),
})
}
#[doc(hidden)]
pub fn new_connect<T>(fields: Vec<(u8, String)>) -> result::Result<T, ConnectError> {
match DbError::new_raw(fields) {
Ok(err) => Err(ConnectError::DbError(err)),
Err(()) => Err(ConnectError::BadResponse),
}
}
#[doc(hidden)]
pub fn new<T>(fields: Vec<(u8, String)>) -> Result<T> {
match DbError::new_raw(fields) {
Ok(err) => Err(Error::DbError(err)),
Err(()) => Err(Error::BadData),
}
}
}
impl fmt::Show for DbError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}: {}", self.severity, self.message)
}
}
impl error::Error for DbError {
fn description(&self) -> &str {
&*self.message
}
fn detail(&self) -> Option<String> {
self.detail.clone()
}
}
#[deriving(Clone, PartialEq, Eq)]
pub enum Error {
DbError(DbError),
IoError(IoError),
StreamDesynchronized,
WrongConnection,
WrongParamCount {
expected: uint,
actual: uint,
},
WrongType(Type),
InvalidColumn,
WasNull,
WrongTransaction,
BadResponse,
BadData,
}
impl fmt::Show for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::DbError(ref err) => err.fmt(fmt),
Error::IoError(ref err) => err.fmt(fmt),
Error::StreamDesynchronized =>
write!(fmt, "Communication with the server has desynchronized due to an earlier \
IO error"),
Error::WrongConnection =>
write!(fmt, "A statement was executed with a connection it was not prepared with"),
Error::WrongParamCount { expected, actual } =>
write!(fmt, "Expected {} parameters but got {}", expected, actual),
Error::WrongType(ref ty) => write!(fmt, "Unexpected type {}", ty),
Error::InvalidColumn => write!(fmt, "Invalid column"),
Error::WasNull => write!(fmt, "The value was NULL"),
Error::WrongTransaction =>
write!(fmt, "An attempt was made to start a transaction or execute a lazy query \
on an object other than the active transaction"),
Error::BadResponse =>
write!(fmt, "The server returned an unexpected response"),
Error::BadData =>
write!(fmt, "The server provided data that the client could not parse"),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::DbError(_) => "An error reported by the Postgres server",
Error::IoError(_) => "An error communicating with the Postgres server",
Error::StreamDesynchronized => {
"Communication with the server has desynchronized due to an earlier IO error"
}
Error::WrongConnection => {
"A statement was executed with a connection with which it was not prepared"
}
Error::WrongParamCount { .. } => "Wrong number of parameters",
Error::WrongType(_) => "Unexpected type",
Error::InvalidColumn => "Invalid column",
Error::WasNull => "The value was NULL",
Error::WrongTransaction => {
"An attempt was made to start a transaction or execute a lazy query on an object \
other than the active transaction"
}
Error::BadResponse => "The server returned an unexpected response",
Error::BadData => "The server provided data that the client could not parse",
}
}
fn detail(&self) -> Option<String> {
match *self {
Error::WrongParamCount { expected, actual } => {
Some(format!("expected: {}, actual: {}", expected, actual))
}
Error::WrongType(ref ty) => Some(format!("saw type {}", ty)),
_ => None
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::DbError(ref err) => Some(err as &error::Error),
Error::IoError(ref err) => Some(err as &error::Error),
_ => None
}
}
}
impl error::FromError<DbError> for Error {
fn from_error(err: DbError) -> Error {
Error::DbError(err)
}
}
impl error::FromError<IoError> for Error {
fn from_error(err: IoError) -> Error {
Error::IoError(err)
}
}