Kit.Core/LibExternal/Npgsql/BackendMessages/ErrorOrNoticeMessage.cs

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'
}
}