using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using JetBrains.Annotations;
using Npgsql.BackendMessages;
#pragma warning disable CA1032
namespace Npgsql
{
///
/// The exception that is thrown when the PostgreSQL backend reports errors (e.g. query
/// SQL issues, constraint violations).
///
///
/// This exception only corresponds to a PostgreSQL-delivered error.
/// Other errors (e.g. network issues) will be raised via ,
/// and purely Npgsql-related issues which aren't related to the server will be raised
/// via the standard CLR exceptions (e.g. ArgumentException).
///
/// See http://www.postgresql.org/docs/current/static/errcodes-appendix.html,
/// http://www.postgresql.org/docs/current/static/protocol-error-fields.html
///
[Serializable]
public sealed class PostgresException : NpgsqlException
{
///
/// Creates a new instance.
///
///
/// Exists for backwards compat with 4.0, has been removed for 5.0.
///
[Obsolete]
public PostgresException() : this(string.Empty, string.Empty, string.Empty, string.Empty) {}
///
/// Creates a new instance.
///
public PostgresException(string messageText, string severity, string invariantSeverity, string sqlState)
{
MessageText = messageText;
Severity = severity;
InvariantSeverity = invariantSeverity;
SqlState = sqlState;
}
PostgresException(ErrorOrNoticeMessage msg)
{
Severity = msg.Severity;
InvariantSeverity = msg.InvariantSeverity;
SqlState = msg.Code;
MessageText = msg.Message;
Detail = msg.Detail;
Hint = msg.Hint;
Position = msg.Position;
InternalPosition = msg.InternalPosition;
InternalQuery = msg.InternalQuery;
Where = msg.Where;
SchemaName = msg.SchemaName;
TableName = msg.TableName;
ColumnName = msg.ColumnName;
DataTypeName = msg.DataTypeName;
ConstraintName = msg.ConstraintName;
File = msg.File;
Line = msg.Line;
Routine = msg.Routine;
AddData(nameof(Severity), Severity);
AddData(nameof(InvariantSeverity), InvariantSeverity);
AddData(nameof(SqlState), SqlState);
AddData(nameof(MessageText), MessageText);
AddData(nameof(Detail), Detail);
AddData(nameof(Hint), Hint);
AddData(nameof(Position), Position);
AddData(nameof(InternalPosition), InternalPosition);
AddData(nameof(InternalQuery), InternalQuery);
AddData(nameof(Where), Where);
AddData(nameof(SchemaName), SchemaName);
AddData(nameof(TableName), TableName);
AddData(nameof(ColumnName), ColumnName);
AddData(nameof(DataTypeName), DataTypeName);
AddData(nameof(ConstraintName), ConstraintName);
AddData(nameof(File), File);
AddData(nameof(Line), Line);
AddData(nameof(Routine), Routine);
void AddData(string key, T value)
{
if (!EqualityComparer.Default.Equals(value, default!))
Data.Add(key, value);
}
}
internal static PostgresException Load(NpgsqlReadBuffer buf)
=> new PostgresException(ErrorOrNoticeMessage.Load(buf));
internal PostgresException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
Severity = GetValue(nameof(Severity));
InvariantSeverity = GetValue(nameof(InvariantSeverity));
SqlState = GetValue(nameof(SqlState));
MessageText = GetValue(nameof(MessageText));
Detail = GetValue(nameof(Detail));
Hint = GetValue(nameof(Hint));
Position = GetValue(nameof(Position));
InternalPosition = GetValue(nameof(InternalPosition));
InternalQuery = GetValue(nameof(InternalQuery));
Where = GetValue(nameof(Where));
SchemaName = GetValue(nameof(SchemaName));
TableName = GetValue(nameof(TableName));
ColumnName = GetValue(nameof(ColumnName));
DataTypeName = GetValue(nameof(DataTypeName));
ConstraintName = GetValue(nameof(ConstraintName));
File = GetValue(nameof(File));
Line = GetValue(nameof(Line));
Routine = GetValue(nameof(Routine));
T GetValue(string propertyName) => (T)info.GetValue(propertyName, typeof(T))!;
}
///
/// Populates a with the data needed to serialize the target object.
///
/// The to populate with data.
/// The destination (see ) for this serialization.
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(nameof(Severity), Severity);
info.AddValue(nameof(InvariantSeverity), InvariantSeverity);
info.AddValue(nameof(SqlState), SqlState);
info.AddValue(nameof(MessageText), MessageText);
info.AddValue(nameof(Detail), Detail);
info.AddValue(nameof(Hint), Hint);
info.AddValue(nameof(Position), Position);
info.AddValue(nameof(InternalPosition), InternalPosition);
info.AddValue(nameof(InternalQuery), InternalQuery);
info.AddValue(nameof(Where), Where);
info.AddValue(nameof(SchemaName), SchemaName);
info.AddValue(nameof(TableName), TableName);
info.AddValue(nameof(ColumnName), ColumnName);
info.AddValue(nameof(DataTypeName), DataTypeName);
info.AddValue(nameof(ConstraintName), ConstraintName);
info.AddValue(nameof(File), File);
info.AddValue(nameof(Line), Line);
info.AddValue(nameof(Routine), Routine);
}
///
public override string ToString()
{
var builder = new StringBuilder(base.ToString())
.AppendLine().Append(" Exception data:");
AppendLine(nameof(Severity), Severity);
AppendLine(nameof(SqlState), SqlState);
AppendLine(nameof(MessageText), MessageText);
AppendLine(nameof(Detail), Detail);
AppendLine(nameof(Hint), Hint);
AppendLine(nameof(Position), Position);
AppendLine(nameof(InternalPosition), InternalPosition);
AppendLine(nameof(InternalQuery), InternalQuery);
AppendLine(nameof(Where), Where);
AppendLine(nameof(SchemaName), SchemaName);
AppendLine(nameof(TableName), TableName);
AppendLine(nameof(ColumnName), ColumnName);
AppendLine(nameof(DataTypeName), DataTypeName);
AppendLine(nameof(ConstraintName), ConstraintName);
AppendLine(nameof(File), File);
AppendLine(nameof(Line), Line);
AppendLine(nameof(Routine), Routine);
return builder.ToString();
void AppendLine(string propertyName, T propertyValue)
{
if (!EqualityComparer.Default.Equals(propertyValue, default!))
builder.AppendLine().Append(" ").Append(propertyName).Append(": ").Append(propertyValue);
}
}
///
/// Gets a the PostgreSQL error message and code.
///
public override string Message => SqlState + ": " + MessageText;
///
/// Specifies whether the exception is considered transient, that is, whether retrying to operation could
/// succeed (e.g. a network error). Check .
///
public override bool IsTransient
{
get
{
switch (SqlState)
{
case PostgresErrorCodes.InsufficientResources:
case PostgresErrorCodes.DiskFull:
case PostgresErrorCodes.OutOfMemory:
case PostgresErrorCodes.TooManyConnections:
case PostgresErrorCodes.ConfigurationLimitExceeded:
case PostgresErrorCodes.CannotConnectNow:
case PostgresErrorCodes.SystemError:
case PostgresErrorCodes.IoError:
case PostgresErrorCodes.SerializationFailure:
case PostgresErrorCodes.LockNotAvailable:
case PostgresErrorCodes.ObjectInUse:
case PostgresErrorCodes.ObjectNotInPrerequisiteState:
case PostgresErrorCodes.ConnectionException:
case PostgresErrorCodes.ConnectionDoesNotExist:
case PostgresErrorCodes.ConnectionFailure:
case PostgresErrorCodes.SqlClientUnableToEstablishSqlConnection:
case PostgresErrorCodes.SqlServerRejectedEstablishmentOfSqlConnection:
case PostgresErrorCodes.TransactionResolutionUnknown:
return true;
default:
return false;
}
}
}
///
/// Returns the statement which triggered this exception.
///
public NpgsqlStatement? Statement { get; internal set; }
#region Message Fields
///
/// Severity of the error or notice.
/// Always present.
///
[PublicAPI]
public string Severity { get; }
///
/// Severity of the error or notice, not localized.
/// Always present since PostgreSQL 9.6.
///
[PublicAPI]
public string InvariantSeverity { get; }
///
/// The SQLSTATE code for the error.
///
///
/// Always present.
/// Constants are defined in .
/// See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
///
[PublicAPI]
public string SqlState { get; }
///
/// The SQLSTATE code for the error.
///
///
/// Always present.
/// Constants are defined in .
/// See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
///
[PublicAPI, Obsolete("Use SqlState instead")]
public string Code => SqlState;
///
/// The primary human-readable error message. This should be accurate but terse.
///
///
/// Always present.
///
[PublicAPI]
public string MessageText { get; }
///
/// An optional secondary error message carrying more detail about the problem.
/// May run to multiple lines.
///
[PublicAPI]
public string? Detail { get; }
///
/// An optional suggestion what to do about the problem.
/// This is intended to differ from Detail in that it offers advice (potentially inappropriate) rather than hard facts.
/// May run to multiple lines.
///
[PublicAPI]
public string? Hint { get; }
///
/// The field value is a decimal ASCII integer, indicating an error cursor position as an index into the original query string.
/// The first character has index 1, and positions are measured in characters not bytes.
/// 0 means not provided.
///
[PublicAPI]
public int Position { get; }
///
/// This is defined the same as the field, but it is used when the cursor position refers to an internally generated command rather than the one submitted by the client.
/// The field will always appear when this field appears.
/// 0 means not provided.
///
[PublicAPI]
public int InternalPosition { get; }
///
/// The text of a failed internally-generated command.
/// This could be, for example, a SQL query issued by a PL/pgSQL function.
///
[PublicAPI]
public string? InternalQuery { get; }
///
/// An indication of the context in which the error occurred.
/// Presently this includes a call stack traceback of active PL functions.
/// The trace is one entry per line, most recent first.
///
[PublicAPI]
public string? Where { get; }
///
/// If the error was associated with a specific database object, the name of the schema containing that object, if any.
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? SchemaName { get; }
///
/// Table name: if the error was associated with a specific table, the name of the table.
/// (Refer to the schema name field for the name of the table's schema.)
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? TableName { get; }
///
/// If the error was associated with a specific table column, the name of the column.
/// (Refer to the schema and table name fields to identify the table.)
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? ColumnName { get; }
///
/// If the error was associated with a specific data type, the name of the data type.
/// (Refer to the schema name field for the name of the data type's schema.)
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? DataTypeName { get; }
///
/// If the error was associated with a specific constraint, the name of the constraint.
/// Refer to fields listed above for the associated table or domain.
/// (For this purpose, indexes are treated as constraints, even if they weren't created with constraint syntax.)
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? ConstraintName { get; }
///
/// The file name of the source-code location where the error was reported.
///
/// PostgreSQL 9.3 and up.
[PublicAPI]
public string? File { get; }
///
/// The line number of the source-code location where the error was reported.
///
[PublicAPI]
public string? Line { get; }
///
/// The name of the source-code routine reporting the error.
///
[PublicAPI]
public string? Routine { get; }
#endregion
}
}