188 lines
7.6 KiB
C#
188 lines
7.6 KiB
C#
using System;
|
|
using Npgsql.Internal;
|
|
using Npgsql.Logging;
|
|
|
|
namespace Npgsql.BackendMessages;
|
|
|
|
[Serializable]
|
|
class ErrorOrNoticeMessage
|
|
{
|
|
internal string Severity { get; }
|
|
internal string InvariantSeverity { get; }
|
|
internal string SqlState { get; }
|
|
internal string Message { get; }
|
|
internal string? Detail { get; }
|
|
internal string? Hint { get; }
|
|
internal int Position { get; }
|
|
internal int InternalPosition { get; }
|
|
internal string? InternalQuery { get; }
|
|
internal string? Where { get; }
|
|
internal string? SchemaName { get; }
|
|
internal string? TableName { get; }
|
|
internal string? ColumnName { get; }
|
|
internal string? DataTypeName { get; }
|
|
internal string? ConstraintName { get; }
|
|
internal string? File { get; }
|
|
internal string? Line { get; }
|
|
internal string? Routine { get; }
|
|
|
|
static readonly NpgsqlLogger Log = NpgsqlLogManager.CreateLogger(nameof(ErrorOrNoticeMessage));
|
|
|
|
// ReSharper disable once FunctionComplexityOverflow
|
|
internal static ErrorOrNoticeMessage Load(NpgsqlReadBuffer buf, bool includeDetail)
|
|
{
|
|
(string? severity, string? invariantSeverity, string? code, string? message, string? detail, string? hint) = (null, null, null, null, null, null);
|
|
var (position, internalPosition) = (0, 0);
|
|
(string? internalQuery, string? where) = (null, null);
|
|
(string? schemaName, string? tableName, string? columnName, string? dataTypeName, string? constraintName) =
|
|
(null, null, null, null, null);
|
|
(string? file, string? line, string? routine) = (null, null, null);
|
|
|
|
while (true)
|
|
{
|
|
var fieldCode = (ErrorFieldTypeCode)buf.ReadByte();
|
|
switch (fieldCode) {
|
|
case ErrorFieldTypeCode.Done:
|
|
// Null terminator; error message fully consumed.
|
|
goto End;
|
|
case ErrorFieldTypeCode.Severity:
|
|
severity = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.InvariantSeverity:
|
|
invariantSeverity = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Code:
|
|
code = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Message:
|
|
message = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Detail:
|
|
detail = buf.ReadNullTerminatedStringRelaxed();
|
|
if (!includeDetail && !string.IsNullOrEmpty(detail))
|
|
detail = $"Detail redacted as it may contain sensitive data. Specify '{NpgsqlConnectionStringBuilder.IncludeExceptionDetailDisplayName}' in the connection string to include this information.";
|
|
break;
|
|
case ErrorFieldTypeCode.Hint:
|
|
hint = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Position:
|
|
var positionStr = buf.ReadNullTerminatedStringRelaxed();
|
|
if (!int.TryParse(positionStr, out var tmpPosition)) {
|
|
Log.Warn("Non-numeric position in ErrorResponse: " + positionStr);
|
|
continue;
|
|
}
|
|
position = tmpPosition;
|
|
break;
|
|
case ErrorFieldTypeCode.InternalPosition:
|
|
var internalPositionStr = buf.ReadNullTerminatedStringRelaxed();
|
|
if (!int.TryParse(internalPositionStr, out var internalPositionTmp)) {
|
|
Log.Warn("Non-numeric position in ErrorResponse: " + internalPositionStr);
|
|
continue;
|
|
}
|
|
internalPosition = internalPositionTmp;
|
|
break;
|
|
case ErrorFieldTypeCode.InternalQuery:
|
|
internalQuery = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Where:
|
|
where = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.File:
|
|
file = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Line:
|
|
line = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.Routine:
|
|
routine = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.SchemaName:
|
|
schemaName = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.TableName:
|
|
tableName = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.ColumnName:
|
|
columnName = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.DataTypeName:
|
|
dataTypeName = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
case ErrorFieldTypeCode.ConstraintName:
|
|
constraintName = buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
default:
|
|
// Unknown error field; consume and discard.
|
|
buf.ReadNullTerminatedStringRelaxed();
|
|
break;
|
|
}
|
|
}
|
|
|
|
End:
|
|
if (severity == null)
|
|
throw new NpgsqlException("Severity not received in server error message");
|
|
if (code == null)
|
|
throw new NpgsqlException("Code not received in server error message");
|
|
if (message == null)
|
|
throw new NpgsqlException("Message not received in server error message");
|
|
|
|
return new ErrorOrNoticeMessage(
|
|
severity, invariantSeverity ?? severity, code, message,
|
|
detail, hint, position, internalPosition, internalQuery, where,
|
|
schemaName, tableName, columnName, dataTypeName, constraintName,
|
|
file, line, routine);
|
|
|
|
}
|
|
|
|
internal ErrorOrNoticeMessage(
|
|
string severity, string invariantSeverity, string sqlState, string message,
|
|
string? detail = null, string? hint = null, int position = 0, int internalPosition = 0, string? internalQuery = null, string? where = null,
|
|
string? schemaName = null, string? tableName = null, string? columnName = null, string? dataTypeName = null, string? constraintName = null,
|
|
string? file = null, string? line = null, string? routine = null)
|
|
{
|
|
Severity = severity;
|
|
InvariantSeverity = invariantSeverity;
|
|
SqlState = sqlState;
|
|
Message = message;
|
|
Detail = detail;
|
|
Hint = hint;
|
|
Position = position;
|
|
InternalPosition = internalPosition;
|
|
InternalQuery = internalQuery;
|
|
Where = where;
|
|
SchemaName = schemaName;
|
|
TableName = tableName;
|
|
ColumnName = columnName;
|
|
DataTypeName = dataTypeName;
|
|
ConstraintName = constraintName;
|
|
File = file;
|
|
Line = line;
|
|
Routine = routine;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Error and notice message field codes
|
|
/// </summary>
|
|
internal enum ErrorFieldTypeCode : byte
|
|
{
|
|
Done = 0,
|
|
Severity = (byte)'S',
|
|
InvariantSeverity = (byte)'V',
|
|
Code = (byte)'C',
|
|
Message = (byte)'M',
|
|
Detail = (byte)'D',
|
|
Hint = (byte)'H',
|
|
Position = (byte)'P',
|
|
InternalPosition = (byte)'p',
|
|
InternalQuery = (byte)'q',
|
|
Where = (byte)'W',
|
|
SchemaName = (byte)'s',
|
|
TableName = (byte)'t',
|
|
ColumnName = (byte)'c',
|
|
DataTypeName = (byte)'d',
|
|
ConstraintName = (byte)'n',
|
|
File = (byte)'F',
|
|
Line = (byte)'L',
|
|
Routine = (byte)'R'
|
|
}
|
|
} |