using Npgsql.Internal; using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Npgsql.Util; static class Statics { #if DEBUG internal static bool LegacyTimestampBehavior; internal static bool DisableDateTimeInfinityConversions; #else internal static readonly bool LegacyTimestampBehavior; internal static readonly bool DisableDateTimeInfinityConversions; #endif static Statics() { LegacyTimestampBehavior = AppContext.TryGetSwitch("Npgsql.EnableLegacyTimestampBehavior", out var enabled) && enabled; DisableDateTimeInfinityConversions = AppContext.TryGetSwitch("Npgsql.DisableDateTimeInfinityConversions", out enabled) && enabled; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static T Expect(IBackendMessage msg, NpgsqlConnector connector) { if (msg is T asT) return asT; throw connector.Break( new NpgsqlException($"Received backend message {msg.Code} while expecting {typeof(T).Name}. " + "Please file a bug.")); } internal static DeferDisposable Defer(Action action) => new(action); internal static DeferDisposable Defer(Action action, T arg) => new(action, arg); internal static DeferDisposable Defer(Action action, T1 arg1, T2 arg2) => new(action, arg1, arg2); // internal static AsyncDeferDisposable DeferAsync(Func func) => new AsyncDeferDisposable(func); internal static AsyncDeferDisposable DeferAsync(Func func) => new(func); internal readonly struct DeferDisposable : IDisposable { readonly Action _action; public DeferDisposable(Action action) => _action = action; public void Dispose() => _action(); } internal readonly struct DeferDisposable : IDisposable { readonly Action _action; readonly T _arg; public DeferDisposable(Action action, T arg) { _action = action; _arg = arg; } public void Dispose() => _action(_arg); } internal readonly struct DeferDisposable : IDisposable { readonly Action _action; readonly T1 _arg1; readonly T2 _arg2; public DeferDisposable(Action action, T1 arg1, T2 arg2) { _action = action; _arg1 = arg1; _arg2 = arg2; } public void Dispose() => _action(_arg1, _arg2); } internal readonly struct AsyncDeferDisposable : IAsyncDisposable { readonly Func _func; public AsyncDeferDisposable(Func func) => _func = func; public async ValueTask DisposeAsync() => await _func(); } } // ReSharper disable once InconsistentNaming static class PGUtil { internal static readonly UTF8Encoding UTF8Encoding = new(false, true); internal static readonly UTF8Encoding RelaxedUTF8Encoding = new(false, false); internal const int BitsInInt = sizeof(int) * 8; internal static void ValidateBackendMessageCode(BackendMessageCode code) { switch (code) { case BackendMessageCode.AuthenticationRequest: case BackendMessageCode.BackendKeyData: case BackendMessageCode.BindComplete: case BackendMessageCode.CloseComplete: case BackendMessageCode.CommandComplete: case BackendMessageCode.CopyData: case BackendMessageCode.CopyDone: case BackendMessageCode.CopyBothResponse: case BackendMessageCode.CopyInResponse: case BackendMessageCode.CopyOutResponse: case BackendMessageCode.DataRow: case BackendMessageCode.EmptyQueryResponse: case BackendMessageCode.ErrorResponse: case BackendMessageCode.FunctionCall: case BackendMessageCode.FunctionCallResponse: case BackendMessageCode.NoData: case BackendMessageCode.NoticeResponse: case BackendMessageCode.NotificationResponse: case BackendMessageCode.ParameterDescription: case BackendMessageCode.ParameterStatus: case BackendMessageCode.ParseComplete: case BackendMessageCode.PasswordPacket: case BackendMessageCode.PortalSuspended: case BackendMessageCode.ReadyForQuery: case BackendMessageCode.RowDescription: return; default: throw new NpgsqlException("Unknown message code: " + code); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int RotateShift(int val, int shift) => (val << shift) | (val >> (BitsInInt - shift)); internal static readonly Task TrueTask = Task.FromResult(true); internal static readonly Task FalseTask = Task.FromResult(false); } enum FormatCode : short { Text = 0, Binary = 1 } static class EnumerableExtensions { internal static string Join(this IEnumerable values, string separator) { return string.Join(separator, values); } } static class ExceptionExtensions { internal static Exception UnwrapAggregate(this Exception exception) => exception is AggregateException agg ? agg.InnerException! : exception; } /// /// Represents a timeout that will expire at some point. /// public readonly struct NpgsqlTimeout { readonly DateTime _expiration; internal static NpgsqlTimeout Infinite = new(TimeSpan.Zero); internal NpgsqlTimeout(TimeSpan expiration) => _expiration = expiration > TimeSpan.Zero ? DateTime.UtcNow + expiration : expiration == TimeSpan.Zero ? DateTime.MaxValue : DateTime.MinValue; internal void Check() { if (HasExpired) throw new TimeoutException(); } internal void CheckAndApply(NpgsqlConnector connector) { if (!IsSet) return; var timeLeft = CheckAndGetTimeLeft(); // Set the remaining timeout on the read and write buffers connector.ReadBuffer.Timeout = connector.WriteBuffer.Timeout = timeLeft; // Note that we set UserTimeout as well, otherwise the read timeout will get overwritten in ReadMessage // Note also that we must set the read buffer's timeout directly (above), since the SSL handshake // reads data directly from the buffer, without going through ReadMessage. connector.UserTimeout = (int) Math.Ceiling(timeLeft.TotalMilliseconds); } internal bool IsSet => _expiration != DateTime.MaxValue; internal bool HasExpired => DateTime.UtcNow >= _expiration; internal TimeSpan CheckAndGetTimeLeft() { if (!IsSet) return Timeout.InfiniteTimeSpan; var timeLeft = _expiration - DateTime.UtcNow; if (timeLeft <= TimeSpan.Zero) Check(); return timeLeft; } } static class MethodInfos { internal static readonly ConstructorInfo InvalidCastExceptionCtor = typeof(InvalidCastException).GetConstructor(new[] { typeof(string) })!; internal static readonly MethodInfo StringFormat = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(string), typeof(object) })!; internal static readonly MethodInfo ObjectGetType = typeof(object).GetMethod(nameof(GetType), new Type[0])!; }