Skip to content
Permalink
Browse files

Merge branch 'hotfix/4.0.8'

  • Loading branch information...
YohDeadfall committed Jul 18, 2019
2 parents 4ae8325 + 7f1802a commit 9c1a1d58a39cffa8d983abb98cc1771a76a64800
@@ -1,5 +1,5 @@
image: Visual Studio 2017
version: 4.0.7-{build}
version: 4.0.8-{build}
environment:
global:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -9,6 +9,7 @@
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_SINGLE_LINE_ARRAY_INITIALIZER_BRACES/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PGTZ/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GIS/@EntryIndexedValue">GIS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GSS/@EntryIndexedValue">GSS</s:String>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>GeoJSON plugin for Npgsql, allowing mapping of PostGIS geometry types to GeoJSON types.</Description>
<Authors>Yoh Deadfall, Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>Json.NET plugin for Npgsql, allowing transparent serialization/deserialization of JSON objects directly to and from the database.</Description>
<Authors>Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>PostGIS plugin for Npgsql, allowing mapping of PostGIS types to the legacy types (e.g. PostgisPoint).</Description>
<Authors>Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>NetTopologySuite plugin for Npgsql, allowing mapping of PostGIS geometry types to NetTopologySuite types.</Description>
<Authors>Yoh Deadfall, Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>NodaTime plugin for Npgsql, allowing mapping of PostgreSQL date/time types to NodaTime types.</Description>
<Authors>Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<Description>PostGIS plugin for Npgsql, allowing raw byte access to PostGIS ypes.</Description>
<Authors>Shay Rojansky</Authors>
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
@@ -1,4 +1,4 @@
#region License
#region License
// The PostgreSQL License
//
// Copyright (C) 2018 The Npgsql Development Team
@@ -84,7 +84,7 @@ internal struct PoolState
[FieldOffset(2)]
internal short Busy;
[FieldOffset(4)]
internal short Waiting;
internal int Waiting;
[FieldOffset(0)]
internal long All;

@@ -108,6 +108,7 @@ public override string ToString()
/// </summary>
int _clearCounter;

static readonly TimerCallback PruningTimerCallback = PruneIdleConnectors;
[CanBeNull]
Timer _pruningTimer;
readonly TimeSpan _pruningInterval;
@@ -123,6 +124,9 @@ public override string ToString()

internal ConnectorPool(NpgsqlConnectionStringBuilder settings, string connString)
{
Debug.Assert(PoolSizeLimit <= short.MaxValue,
"PoolSizeLimit cannot be larger than short.MaxValue unless PoolState is refactored to hold larger values.");

if (settings.MaxPoolSize < settings.MinPoolSize)
throw new ArgumentException($"Connection can't have MaxPoolSize {settings.MaxPoolSize} under MinPoolSize {settings.MinPoolSize}");

@@ -306,7 +310,7 @@ internal async ValueTask<NpgsqlConnector> AllocateLong(NpgsqlConnection conn, Np
// Start the pruning timer if we're above MinPoolSize
if (_pruningTimer == null && newState.Total > _min)
{
var newPruningTimer = new Timer(PruneIdleConnectors);
var newPruningTimer = new Timer(PruningTimerCallback, this, -1, -1);
if (Interlocked.CompareExchange(ref _pruningTimer, newPruningTimer, null) == null)
newPruningTimer.Change(_pruningInterval, _pruningInterval);
else
@@ -323,7 +327,11 @@ internal async ValueTask<NpgsqlConnector> AllocateLong(NpgsqlConnection conn, Np
{
// Pool is exhausted. Increase the waiting count while atomically making sure the busy count
// doesn't decrease (otherwise we have a new idle connector).
newState.Waiting++;
checked
{
newState.Waiting++;
}

CheckInvariants(newState);
if (Interlocked.CompareExchange(ref State.All, newState.All, state.All) != state.All)
{
@@ -351,7 +359,7 @@ internal async ValueTask<NpgsqlConnector> AllocateLong(NpgsqlConnection conn, Np
// Use Task.Delay to implement the timeout, but cancel the timer if we actually
// do complete successfully
var delayCancellationToken = new CancellationTokenSource();
using (cancellationToken.Register(() => delayCancellationToken.Cancel()))
using (cancellationToken.Register(s => ((CancellationTokenSource)s).Cancel(), delayCancellationToken))
{
var timeLeft = timeout.TimeLeft;
if (timeLeft <= TimeSpan.Zero ||
@@ -366,7 +374,7 @@ await Task.WhenAny(tcs.Task, Task.Delay(timeLeft, delayCancellationToken.Token))
}
else
{
using (cancellationToken.Register(() => tcs.SetCanceled()))
using (cancellationToken.Register(s => ((TaskCompletionSource<NpgsqlConnector>)s).SetCanceled(), tcs))
await tcs.Task;
}
}
@@ -597,21 +605,23 @@ void CloseConnector(NpgsqlConnector connector, bool wasIdle)
}
}

void PruneIdleConnectors(object _)
static void PruneIdleConnectors(object state)
{
var pool = (ConnectorPool)state;
var idle = pool._idle;
var now = DateTime.UtcNow;
var idleLifetime = Settings.ConnectionIdleLifetime;
var idleLifetime = pool.Settings.ConnectionIdleLifetime;

for (var i = 0; i < _idle.Length; i++)
for (var i = 0; i < idle.Length; i++)
{
if (State.Total <= _min)
if (pool.State.Total <= pool._min)
return;

var connector = _idle[i];
var connector = idle[i];
if (connector == null || (now - connector.ReleaseTimestamp).TotalSeconds < idleLifetime)
continue;
if (Interlocked.CompareExchange(ref _idle[i], null, connector) == connector)
CloseConnector(connector, true);
if (Interlocked.CompareExchange(ref idle[i], null, connector) == connector)
pool.CloseConnector(connector, true);
}
}

@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Npgsql
namespace Npgsql
{
/// <summary>
/// A component which translates a CLR name (e.g. SomeClass) into a database name (e.g. some_class)
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Npgsql.NameTranslation
{
@@ -14,11 +10,11 @@ public class NpgsqlNullNameTranslator : INpgsqlNameTranslator
/// <summary>
/// Given a CLR type name (e.g class, struct, enum), translates its name to a database type name.
/// </summary>
public string TranslateTypeName(string clrName) => clrName;
public string TranslateTypeName(string clrName) => clrName ?? throw new ArgumentNullException(nameof(clrName));

/// <summary>
/// Given a CLR member name (property or field), translates its name to a database type name.
/// </summary>
public string TranslateMemberName(string clrName) => clrName;
public string TranslateMemberName(string clrName) => clrName ?? throw new ArgumentNullException(nameof(clrName));
}
}
@@ -21,6 +21,7 @@
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#endregion

using System;
using System.Linq;
using System.Text;

@@ -54,9 +55,15 @@ public NpgsqlSnakeCaseNameTranslator(bool legacyMode)
/// <summary>
/// Given a CLR member name (property or field), translates its name to a database type name.
/// </summary>
public string TranslateMemberName(string clrName) => LegacyMode
? string.Concat(clrName.Select((c, i) => i > 0 && char.IsUpper(c) ? "_" + c.ToString() : c.ToString())).ToLower()
: ConvertToSnakeCase(clrName);
public string TranslateMemberName(string clrName)
{
if (clrName == null)
throw new ArgumentNullException(nameof(clrName));

return LegacyMode
? string.Concat(clrName.Select((c, i) => i > 0 && char.IsUpper(c) ? "_" + c.ToString() : c.ToString())).ToLower()
: ConvertToSnakeCase(clrName);
}

/// <summary>
/// Converts a string to its snake_case equivalent.
@@ -68,9 +75,6 @@ public NpgsqlSnakeCaseNameTranslator(bool legacyMode)
/// <param name="value">The value to convert.</param>
public static string ConvertToSnakeCase(string value)
{
if (string.IsNullOrEmpty(value))
return value;

var sb = new StringBuilder();
var state = SnakeCaseState.Start;

@@ -6,7 +6,7 @@
<Copyright>Copyright 2019 © The Npgsql Development Team</Copyright>
<Company>Npgsql</Company>
<PackageTags>npgsql postgresql postgres ado ado.net database sql</PackageTags>
<VersionPrefix>4.0.7</VersionPrefix>
<VersionPrefix>4.0.8</VersionPrefix>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net45;net451;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' OR '$(CoreOnly)' == 'True'">netstandard2.0</TargetFrameworks>
@@ -1228,6 +1228,9 @@ public bool Wait(int timeout)
[PublicAPI]
public Task WaitAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return PGUtil.CancelledTask;

CheckConnectionOpen();
Debug.Assert(Connector != null);
Log.Debug("Starting to wait asynchronously...", Connector.Id);
@@ -441,7 +441,19 @@ async Task<bool> NextResult(bool async, bool isConsuming=false)
}
}

Expect<ParseCompleteMessage>(await Connector.ReadMessage(async));
try
{
Expect<ParseCompleteMessage>(await Connector.ReadMessage(async));
}
catch
{
// An exception occurred. Check if any statements we being prepared and revert our bookkeeping.
pStatement?.CompleteUnprepare();
throw;
}

pStatement?.CompletePrepare();

Expect<BindCompleteMessage>(await Connector.ReadMessage(async));
msg = await Connector.ReadMessage(async);
switch (msg.Code)
@@ -456,12 +468,6 @@ async Task<bool> NextResult(bool async, bool isConsuming=false)
default:
throw Connector.UnexpectedMessageReceived(msg.Code);
}

if (pStatement != null)
{
Debug.Assert(!pStatement.IsPrepared);
pStatement.CompletePrepare();
}
}

// The following is a pretty awful hack to bring back output parameters for sequential readers (#2091)
@@ -40,39 +40,42 @@ static class PoolManager
internal const int InitialPoolsSize = 10;

static readonly object _lock = new object();
static (string Key, ConnectorPool Pool)[] _pools = new (string, ConnectorPool)[InitialPoolsSize];
static int _nextSlot;
static volatile (string Key, ConnectorPool Pool)[] _pools = new (string, ConnectorPool)[InitialPoolsSize];
static volatile int _nextSlot;

internal static bool TryGetValue(string key, out ConnectorPool pool)
{
// Note that pools never get removed. _pools is strictly append-only.
var nextSlot = _nextSlot;
var pools = _pools;
var sw = new SpinWait();

// First scan the pools and do reference equality on the connection strings
for (var i = 0; i < _nextSlot; i++)
for (var i = 0; i < nextSlot; i++)
{
if (ReferenceEquals(pools[i].Key, key))
var cp = pools[i];
if (ReferenceEquals(cp.Key, key))
{
// It's possible that this pool entry is currently being written: the connection string
// component has already been writte, but the pool component is just about to be. So we
// loop on the pool until it's non-null
while (Volatile.Read(ref pools[i].Pool) == null)
while (Volatile.Read(ref cp.Pool) == null)
sw.SpinOnce();
pool = pools[i].Pool;
pool = cp.Pool;
return true;
}
}

// Next try value comparison on the strings
for (var i = 0; i < _nextSlot; i++)
for (var i = 0; i < nextSlot; i++)
{
if (pools[i].Key == key)
var cp = pools[i];
if (cp.Key == key)
{
// See comment above
while (Volatile.Read(ref pools[i].Pool) == null)
while (Volatile.Read(ref cp.Pool) == null)
sw.SpinOnce();
pool = pools[i].Pool;
pool = cp.Pool;
return true;
}
}
@@ -93,7 +96,7 @@ internal static ConnectorPool GetOrAdd(string key, ConnectorPool pool)
{
var newPools = new (string, ConnectorPool)[_pools.Length * 2];
Array.Copy(_pools, newPools, _pools.Length);
Interlocked.Exchange(ref _pools, newPools);
_pools = newPools;
}

_pools[_nextSlot].Key = key;
@@ -113,11 +116,16 @@ internal static void Clear(string connString)

internal static void ClearAll()
{
for (var i = 0; i < _nextSlot; i++)
lock (_lock)
{
if (_pools[i].Key == null)
return;
_pools[i].Pool?.Clear();
var pools = _pools;
for (var i = 0; i < _nextSlot; i++)
{
var cp = pools[i];
if (cp.Key == null)
return;
cp.Pool?.Clear();
}
}
}

@@ -144,15 +144,14 @@ enum PreparedState

/// <summary>
/// The statement has been selected for preparation, but the preparation hasn't started yet.
/// This is a temporary state that only occurs during preparation.
/// Specifically, no protocol message (Parse) has been sent yet. Specifically, it means that
/// a Parse message for the statement has already been written to the write buffer.
/// This is a temporary state that only occurs during preparation, and indicates that no
/// no protocol message (Parse) has been sent yet.
/// </summary>
ToBePrepared,

/// <summary>
/// The statement is in the process of being prepared. This is a temporary state that only occurs during
/// preparation. Specifically, it means that a Parse message for the statement has already been written
/// preparation, and indicates that a Parse protocol message for the statement has already been written
/// to the write buffer.
/// </summary>
BeingPrepared,

0 comments on commit 9c1a1d5

Please sign in to comment.
You can’t perform that action at this time.