Skip to content

Addressing Issues #196, #192 and #205 #197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/common.props
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<Project>
<PropertyGroup>
<TargetFrameworks>netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1;netcoreapp2.2;net46;netstandard2.0</TargetFrameworks>
<VersionPrefix>0.19.1</VersionPrefix>
<VersionPrefix>0.19.2</VersionPrefix>
<AssemblyVersion>0.11.0.0</AssemblyVersion>
<FileVersion>$(VersionPrefix)</FileVersion>
<Authors>dataaction</Authors>
<PackageTags>Sybase ASE Adaptive SAP AseClient DbProvider</PackageTags>
<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://github.com/DataAction/AdoNetCore.AseClient</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageReleaseNotes>Refer to GitHub - https://github.com/DataAction/AdoNetCore.AseClient/releases/tag/0.19.1</PackageReleaseNotes>
<PackageReleaseNotes>Refer to GitHub - https://github.com/DataAction/AdoNetCore.AseClient/releases/tag/0.19.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/DataAction/AdoNetCore.AseClient</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
Expand Down
7 changes: 5 additions & 2 deletions src/AdoNetCore.AseClient/AseCommandBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,15 @@ private static DataTable GetSchemaTableWithKeyInfo(DbCommand sourceCommand)
if (dataTable != null)
{
// If there is no primary key on the table, then throw MissingPrimaryKeyException.
var isKeyColumn = dataTable.Columns["IsKey"];
var isKeyColumn = dataTable.Columns[SchemaTableColumn.IsKey];
var isUniqueColumn = dataTable.Columns[SchemaTableColumn.IsUnique];

var hasKey = false;

foreach (DataRow columnDescriptorRow in dataTable.Rows)
{
hasKey |= (bool)columnDescriptorRow[isKeyColumn];
hasKey |= (bool)columnDescriptorRow[isKeyColumn] ||
(bool)columnDescriptorRow[isUniqueColumn];

if (hasKey)
{
Expand Down
14 changes: 12 additions & 2 deletions src/AdoNetCore.AseClient/AseConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Data;
using System.Data.Common;
using System.Net.Security;
using AdoNetCore.AseClient.Interface;
using AdoNetCore.AseClient.Internal;

Expand Down Expand Up @@ -274,7 +275,7 @@ public override void Open()
{
var parameters = ConnectionParameters.Parse(_connectionString);

_internal = _connectionPoolManager.Reserve(_connectionString, parameters, _eventNotifier);
_internal = _connectionPoolManager.Reserve(_connectionString, parameters, _eventNotifier, UserCertificateValidationCallback);

InternalConnectionTimeout = parameters.LoginTimeout;
}
Expand Down Expand Up @@ -543,7 +544,10 @@ public bool NamedParameters
#if ENABLE_CLONEABLE_INTERFACE
public object Clone()
{
return new AseConnection(_connectionString, _connectionPoolManager);
return new AseConnection(_connectionString, _connectionPoolManager)
{
UserCertificateValidationCallback = UserCertificateValidationCallback,
};
}
#endif

Expand Down Expand Up @@ -621,6 +625,12 @@ public AseTransaction Transaction
return _transaction;
}
}

/// <summary>
/// Allow consumer to override the default certificate validation
/// </summary>
public RemoteCertificateValidationCallback UserCertificateValidationCallback { get; set; }

}

/// <summary>
Expand Down
28 changes: 18 additions & 10 deletions src/AdoNetCore.AseClient/AseParameterCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@ public AseParameter Add(string parameterName, AseDbType dbType, int size, Parame
/// <returns>A new <see cref="AseParameter" /> object.</returns>
public AseParameter Add(AseParameter parameter)
{
if (parameter != null)
{
_parameters.Add(parameter);
}
if (parameter == null) throw new ArgumentNullException(nameof(parameter));
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter.ParameterName}' is already in the collection");

_parameters.Add(parameter);
return parameter;
}

Expand Down Expand Up @@ -379,6 +379,7 @@ public override int Add(object value)
{
if (value is AseParameter p)
{
if (Contains(p)) throw new ArgumentException($"Parameter name: '{p.ParameterName}' is already registered", nameof(value));
return ((IList)_parameters).Add(p);
}
return -1;
Expand Down Expand Up @@ -438,15 +439,23 @@ public override int IndexOf(object value)
/// Returns -1 when the object does not exist in the <see cref="AseParameterCollection" />.</returns>
public int IndexOf(AseParameter value)
{
if (value != null)
if (value == null) return -1;
for (var i = 0; i < _parameters.Count; i++)
{
for (var i = 0; i < _parameters.Count; i++)
if (string.IsNullOrWhiteSpace(value.ParameterName))
{
if (value == _parameters[i])
{
return i;
}
}
else
{
if (value == _parameters[i] || value.ParameterName == _parameters[i].ParameterName)
{
return i;
}
}
}

return -1;
Expand All @@ -459,6 +468,7 @@ public int IndexOf(AseParameter value)
/// <param name="parameter">The <see cref="AseParameter" /> object to add to the collection.</param>
public override void Insert(int index, object parameter)
{
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter}' is already in the collection");
((IList)_parameters).Insert(index, parameter);
}

Expand All @@ -469,6 +479,7 @@ public override void Insert(int index, object parameter)
/// <param name="parameter">The <see cref="AseParameter" /> object to add to the collection.</param>
public void Insert(int index, AseParameter parameter)
{
if (Contains(parameter)) throw new ArgumentException($"parameter name '{parameter.ParameterName}' is already in the collection");
((IList)_parameters).Insert(index, parameter);
}

Expand Down Expand Up @@ -521,10 +532,7 @@ public override void RemoveAt(int index)
/// <param name="index">The starting index of the array.</param>
public override void CopyTo(Array array, int index)
{
if (array != null)
{
((IList)_parameters).CopyTo(array, index);
}
((IList)_parameters).CopyTo(array, index);
}

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/AdoNetCore.AseClient/Interface/IConnectionPoolManager.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Net.Security;

namespace AdoNetCore.AseClient.Interface
{
internal interface IConnectionPoolManager
{
IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier);
IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier, RemoteCertificateValidationCallback userCertificateValidationCallback = null);
void Release(string connectionString, IInternalConnection connection);
void ClearPool(string connectionString);
void ClearPools();
Expand Down
7 changes: 4 additions & 3 deletions src/AdoNetCore.AseClient/Internal/ConnectionPoolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Security;
using AdoNetCore.AseClient.Interface;

namespace AdoNetCore.AseClient.Internal
Expand All @@ -14,14 +15,14 @@ internal sealed class ConnectionPoolManager : IConnectionPoolManager, IEnumerabl

private static readonly ConcurrentDictionary<string, IConnectionPool> Pools = new ConcurrentDictionary<string, IConnectionPool>(StringComparer.OrdinalIgnoreCase);

public IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier)
public IInternalConnection Reserve(string connectionString, IConnectionParameters parameters, IInfoMessageEventNotifier eventNotifier, RemoteCertificateValidationCallback userCertificateValidationCallback = null)
{
return Pools.GetOrAdd(connectionString, _ =>
{
#if ENABLE_ARRAY_POOL
var internalConnectionFactory = new InternalConnectionFactory(parameters, BufferPool);
var internalConnectionFactory = new InternalConnectionFactory(parameters, BufferPool, userCertificateValidationCallback);
#else
var internalConnectionFactory = new InternalConnectionFactory(parameters);
var internalConnectionFactory = new InternalConnectionFactory(parameters, userCertificateValidationCallback);
#endif

return new ConnectionPool(parameters, internalConnectionFactory);
Expand Down
74 changes: 42 additions & 32 deletions src/AdoNetCore.AseClient/Internal/FormatItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,50 +64,60 @@ public string ParameterName
/// </summary>
public SerializationType SerializationType { get; set; }

public static FormatItem CreateForParameter(AseParameter parameter, DbEnvironment env, CommandType commandType)
public static FormatItem CreateForParameter(AseParameter parameter, DbEnvironment env, AseCommand command)
{
parameter.AseDbType = TypeMap.InferType(parameter);

var dbType = parameter.DbType;

var length = TypeMap.GetFormatLength(dbType, parameter, env.Encoding);

var format = new FormatItem
var format = command.FormatItem;
var parameterName = parameter.ParameterName ?? command.Parameters.IndexOf(parameter).ToString();
if (!(command.FormatItem != null && command.FormatItem.ParameterName == parameterName &&
command.FormatItem.AseDbType == parameter.AseDbType))
{
AseDbType = parameter.AseDbType,
ParameterName = parameter.ParameterName,
IsOutput = parameter.IsOutput,
IsNullable = parameter.IsNullable,
Length = length,
DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName),
UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length)
};
format = new FormatItem
{
AseDbType = parameter.AseDbType,
ParameterName = parameter.ParameterName,
IsOutput = parameter.IsOutput,
IsNullable = parameter.IsNullable,
Length = length,
DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName),
UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length)
};

//fixup the FormatItem's BlobType for strings and byte arrays
if (format.DataType == TdsDataType.TDS_BLOB)
{
switch (parameter.DbType)
//fixup the FormatItem's BlobType for strings and byte arrays
if (format.DataType == TdsDataType.TDS_BLOB)
{
case DbType.AnsiString:
format.BlobType = BlobType.BLOB_LONGCHAR;
break;
case DbType.String:
format.BlobType = BlobType.BLOB_UNICHAR;
// This is far less than ideal but at the time of addressing this issue whereby if the
// BlobType is a BLOB_UNICHAR then the UserType would need to be 36 when it
// is a stored proc otherwise it would need to be zero (0).
//
// In the future, we'd need to overhaul how TDS_BLOB is structured especially
// around BLOB_UNICHAR and the UserType that it should return in a more consistent way
if (commandType != CommandType.StoredProcedure)
format.UserType = 0;
switch (parameter.DbType)
{
case DbType.AnsiString:
format.BlobType = BlobType.BLOB_LONGCHAR;
break;
case DbType.String:
format.BlobType = BlobType.BLOB_UNICHAR;
// This is far less than ideal but at the time of addressing this issue whereby if the
// BlobType is a BLOB_UNICHAR then the UserType would need to be 36 when it
// is a stored proc otherwise it would need to be zero (0).
//
// In the future, we'd need to overhaul how TDS_BLOB is structured especially
// around BLOB_UNICHAR and the UserType that it should return in a more consistent way
if (command.CommandType != CommandType.StoredProcedure)
format.UserType = 0;

break;
case DbType.Binary:
format.BlobType = BlobType.BLOB_LONGBINARY;
break;
break;
case DbType.Binary:
format.BlobType = BlobType.BLOB_LONGBINARY;
break;
}
}
}
else
{
format.DataType = TypeMap.GetTdsDataType(dbType, parameter.SendableValue, length, parameter.ParameterName);
format.UserType = TypeMap.GetUserType(dbType, parameter.SendableValue, length);
}

//fixup the FormatItem's length,scale,precision for decimals
if (format.IsDecimalType)
Expand Down
6 changes: 1 addition & 5 deletions src/AdoNetCore.AseClient/Internal/InternalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,11 +669,7 @@ private IToken[] BuildParameterTokens(AseCommand command)

foreach (var parameter in command.Parameters.SendableParameters)
{
var parameterName = parameter.ParameterName ?? command.Parameters.IndexOf(parameter).ToString();
if (!(command.FormatItem != null && command.FormatItem.ParameterName == parameterName && command.FormatItem.AseDbType == parameter.AseDbType))
{
command.FormatItem = FormatItem.CreateForParameter(parameter, _environment, command.CommandType);
}
command.FormatItem = FormatItem.CreateForParameter(parameter, _environment, command);

formatItems.Add(command.FormatItem);
parameterItems.Add(new ParametersToken.Parameter
Expand Down
14 changes: 10 additions & 4 deletions src/AdoNetCore.AseClient/Internal/InternalConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@ namespace AdoNetCore.AseClient.Internal
internal class InternalConnectionFactory : IInternalConnectionFactory
{
private readonly IConnectionParameters _parameters;
private readonly RemoteCertificateValidationCallback _userCertificateValidationCallback;


#if ENABLE_ARRAY_POOL
private readonly System.Buffers.ArrayPool<byte> _arrayPool;
#endif
private IPEndPoint _endpoint;

#if ENABLE_ARRAY_POOL
public InternalConnectionFactory(IConnectionParameters parameters, System.Buffers.ArrayPool<byte> arrayPool)
public InternalConnectionFactory(IConnectionParameters parameters, System.Buffers.ArrayPool<byte> arrayPool, RemoteCertificateValidationCallback userCertificateValidationCallback)
#else
public InternalConnectionFactory(IConnectionParameters parameters)
public InternalConnectionFactory(IConnectionParameters parameters, RemoteCertificateValidationCallback userCertificateValidationCallback)
#endif
{
_parameters = parameters;
_userCertificateValidationCallback = userCertificateValidationCallback ?? UserCertificateValidationCallback;

#if ENABLE_ARRAY_POOL
_arrayPool = arrayPool;
#endif
Expand Down Expand Up @@ -109,7 +114,7 @@ private InternalConnection CreateConnection(Socket socket, CancellationToken tok

if (_parameters.Encryption)
{
sslStream = new SslStream(networkStream, false, UserCertificateValidationCallback);
sslStream = new SslStream(networkStream, false, _userCertificateValidationCallback);

var authenticate = sslStream.AuthenticateAsClientAsync(_parameters.Server);

Expand Down Expand Up @@ -175,10 +180,11 @@ private InternalConnection CreateConnectionInternal(Stream networkStream)
#if ENABLE_ARRAY_POOL
return new InternalConnection(_parameters, networkStream, reader, environment, _arrayPool);
#else
return new InternalConnection(_parameters, networkStream, reader, environment);
return new InternalConnection(_parameters, networkStream, reader, environment);
#endif
}


private bool UserCertificateValidationCallback(object sender, X509Certificate serverCertificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
var certificateChainPolicyErrors = (sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors;
Expand Down
7 changes: 1 addition & 6 deletions src/AdoNetCore.AseClient/Internal/SchemaTableBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,12 @@ private FillTableResults FillTableFromFormats(DataTable table)
private void TryLoadKeyInfo(DataTable table, string baseTableNameValue, string baseSchemaNameValue, string baseCatalogNameValue)
{
if (_connection == null)
{
throw new InvalidOperationException("Invalid AseCommand.Connection");
}

if (_connection.State != ConnectionState.Open)
{
throw new InvalidOperationException("Invalid AseCommand.Connection.ConnectionState");
}

if (!string.IsNullOrWhiteSpace(baseTableNameValue) &&
!string.IsNullOrWhiteSpace(baseCatalogNameValue))
if (string.IsNullOrWhiteSpace(baseTableNameValue))
return;

using (var command = _connection.CreateCommand())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />

<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
Expand All @@ -34,9 +35,11 @@
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46'">
<PackageReference Include="Sybase.AdoNet4.AseClient">
Expand Down
Loading