// created on 12/7/2003 at 18:36
// Npgsql.NpgsqlError.cs
//
// Author:
// Francisco Jr. (fxjrlists@yahoo.com.br)
//
// Copyright (C) 2002 The Npgsql Development Team
// npgsql-general@gborg.postgresql.org
// http://gborg.postgresql.org/project/npgsql/projdisplay.php
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
using System;
using System.IO;
using System.Text;
namespace Npgsql
{
///
/// EventArgs class to send Notice parameters, which are just NpgsqlError's in a lighter context.
///
public class NpgsqlNoticeEventArgs : EventArgs
{
///
/// Notice information.
///
public NpgsqlError Notice = null;
internal NpgsqlNoticeEventArgs(NpgsqlError eNotice)
{
Notice = eNotice;
}
}
///
/// This class represents the ErrorResponse and NoticeResponse
/// message sent from PostgreSQL server.
///
[Serializable]
public sealed class NpgsqlError
{
///
/// Error and notice message field codes
///
private enum ErrorFieldTypeCodes : byte
{
///
/// Severity: the field contents are ERROR, FATAL, or PANIC (in an error message),
/// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a localized
/// translation of one of these. Always present.
///
Severity = (byte)'S',
///
/// Code: the SQLSTATE code for the error (see Appendix A). Not localizable. Always present.
///
Code = (byte)'C',
///
/// Message: the primary human-readable error message. This should be accurate
/// but terse (typically one line). Always present.
///
Message = (byte)'M',
///
/// Detail: an optional secondary error message carrying more detail about the problem.
/// Might run to multiple lines.
///
Detail = (byte)'D',
///
/// Hint: 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.
/// Might run to multiple lines.
///
Hint = (byte)'H',
///
/// Position: 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.
///
Position = (byte)'P',
///
/// Internal position: this is defined the same as the P field, but it is used when the
/// cursor position refers to an internally generated command rather than the one submitted
/// by the client.
/// The q field will always appear when this field appears.
///
InternalPosition = (byte)'p',
///
/// Internal query: the text of a failed internally-generated command.
/// This could be, for example, a SQL query issued by a PL/pgSQL function.
///
InternalQuery = (byte)'q',
///
/// Where: an indication of the context in which the error occurred.
/// Presently this includes a call stack traceback of active procedural language functions
/// and internally-generated queries. The trace is one entry per line, most recent first.
///
Where = (byte)'W',
///
/// Schema name: if the error was associated with a specific database object,
/// the name of the schema containing that object, if any.
///
SchemaName = (byte)'s',
///
/// 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.)
///
TableName = (byte)'t',
///
/// Column name: 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.)
///
ColumnName = (byte)'c',
///
/// Data type name: 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.)
///
DataTypeName = (byte)'d',
///
/// Constraint name: 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.)
///
ConstraintName = (byte)'n',
///
/// File: the file name of the source-code location where the error was reported.
///
File = (byte)'F',
///
/// Line: the line number of the source-code location where the error was reported.
///
Line = (byte)'L',
///
/// Routine: the name of the source-code routine reporting the error.
///
Routine = (byte)'R'
}
private readonly ProtocolVersion protocol_version;
private readonly String _severity = String.Empty;
private readonly String _code = String.Empty;
private readonly String _message = String.Empty;
private readonly String _detail = String.Empty;
private readonly String _hint = String.Empty;
private readonly String _position = String.Empty;
private readonly String _internalPosition = String.Empty;
private readonly String _internalQuery = String.Empty;
private readonly String _where = String.Empty;
private readonly String _file = String.Empty;
private readonly String _line = String.Empty;
private readonly String _routine = String.Empty;
private readonly String _schemaName = String.Empty;
private readonly String _tableName = String.Empty;
private readonly String _columnName = String.Empty;
private readonly String _datatypeName = String.Empty;
private readonly String _constraintName = String.Empty;
private String _errorSql = String.Empty;
///
/// Severity code. All versions.
///
public String Severity
{
get { return _severity; }
}
///
/// Error code. PostgreSQL 7.4 and up.
///
public String Code
{
get { return _code; }
}
///
/// Terse error message. All versions.
///
public String Message
{
get { return _message; }
}
///
/// Detailed error message. PostgreSQL 7.4 and up.
///
public String Detail
{
get { return _detail; }
}
///
/// Suggestion to help resolve the error. PostgreSQL 7.4 and up.
///
public String Hint
{
get { return _hint; }
}
///
/// Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up.
///
public String Position
{
get { return _position; }
}
///
/// Position (one based) within the query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up.
///
public String InternalPosition
{
get { return _internalPosition; }
}
///
/// Internal query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up.
///
public String InternalQuery
{
get { return _internalQuery; }
}
///
/// Trace back information. PostgreSQL 7.4 and up.
///
public String Where
{
get { return _where; }
}
///
/// Source file (in backend) reporting the error. PostgreSQL 7.4 and up.
///
public String File
{
get { return _file; }
}
///
/// Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up.
///
public String Line
{
get { return _line; }
}
///
/// Source routine (in backend) reporting the error. PostgreSQL 7.4 and up.
///
public String Routine
{
get { return _routine; }
}
///
/// Schema name which relates to the error. PostgreSQL 9.3 and up.
///
public String SchemaName
{
get { return _schemaName; }
}
///
/// Table name which relates to the error. PostgreSQL 9.3 and up.
///
public String TableName
{
get { return _tableName; }
}
///
/// Column name which relates to the error. PostgreSQL 9.3 and up.
///
public String ColumnName
{
get { return _columnName; }
}
///
/// Data type of column which relates to the error. PostgreSQL 9.3 and up.
///
public String DataTypeName
{
get { return _datatypeName; }
}
///
/// Constraint name which relates to the error. PostgreSQL 9.3 and up.
///
public String ConstraintName
{
get { return _constraintName; }
}
///
/// String containing the sql sent which produced this error.
///
public String ErrorSql
{
set { _errorSql = value; }
get { return _errorSql; }
}
///
/// Return a string representation of this error object.
///
public override String ToString()
{
StringBuilder B = new StringBuilder();
if (Severity.Length > 0)
{
B.AppendFormat("{0}: ", Severity);
}
if (Code.Length > 0)
{
B.AppendFormat("{0}: ", Code);
}
B.AppendFormat("{0}", Message);
// CHECKME - possibly multi-line, that is yucky
// if (Hint.Length > 0) {
// B.AppendFormat(" ({0})", Hint);
// }
return B.ToString();
}
internal NpgsqlError(ProtocolVersion protocolVersion, Stream stream)
{
switch (protocol_version = protocolVersion)
{
case ProtocolVersion.Version2:
string[] parts = PGUtil.ReadString(stream).Split(new char[] {':'}, 2);
if (parts.Length == 2)
{
_severity = parts[0].Trim();
_message = parts[1].Trim();
}
else
{
_severity = string.Empty;
_message = parts[0].Trim();
}
break;
case ProtocolVersion.Version3:
// Check the messageLength value. If it is 1178686529, this would be the
// "FATA" string, which would mean a protocol 2.0 error string.
if (PGUtil.ReadInt32(stream) == 1178686529)
{
string[] v2Parts = ("FATA" + PGUtil.ReadString(stream)).Split(new char[] {':'}, 2);
if (v2Parts.Length == 2)
{
_severity = v2Parts[0].Trim();
_message = v2Parts[1].Trim();
}
else
{
_severity = string.Empty;
_message = v2Parts[0].Trim();
}
protocol_version = ProtocolVersion.Version2;
}
else
{
bool done = false;
int fieldCode;
while (! done && (fieldCode = stream.ReadByte()) != -1)
{
switch ((byte)fieldCode)
{
case 0 :
// Null terminator; error message fully consumed.
done = true;
;
break;
case (byte)ErrorFieldTypeCodes.Severity :
_severity = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Code :
_code = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Message :
_message = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Detail :
_detail = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Hint :
_hint = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Position :
_position = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.InternalPosition :
_internalPosition = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.InternalQuery :
_internalQuery = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Where :
_where = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.File :
_file = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Line :
_line = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.Routine :
_routine = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.SchemaName :
_schemaName = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.TableName :
_tableName = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.ColumnName :
_columnName = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.DataTypeName :
_datatypeName = PGUtil.ReadString(stream);
;
break;
case (byte)ErrorFieldTypeCodes.ConstraintName :
_constraintName = PGUtil.ReadString(stream);
;
break;
default:
// Unknown error field; consume and discard.
PGUtil.ReadString(stream);
;
break;
}
}
}
break;
}
}
internal NpgsqlError(ProtocolVersion protocolVersion, String errorMessage)
{
protocol_version = protocolVersion;
_message = errorMessage;
}
///
/// Backend protocol version in use.
///
internal ProtocolVersion BackendProtocolVersion
{
get { return protocol_version; }
}
}
}