diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index e81044196..384c6aed7 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -27,58 +27,58 @@ steps:
displayName: 'Publish MySqlConnector.Tests'
inputs:
command: 'publish'
- arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
+ arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
publishWebProjects: false
zipAfterPublish: false
- task: PublishPipelineArtifact@0
inputs:
- artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
- targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net8.0'
+ artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
+ targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net9.0'
- task: DotNetCoreCLI@2
displayName: 'Publish Conformance.Tests'
inputs:
command: 'publish'
- arguments: '-c Release -f net8.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
+ arguments: '-c Release -f net9.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
publishWebProjects: false
zipAfterPublish: false
- task: PublishPipelineArtifact@0
inputs:
- artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
- targetPath: 'artifacts/publish/Conformance.Tests/release_net8.0'
+ artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
+ targetPath: 'artifacts/publish/Conformance.Tests/release_net9.0'
- task: DotNetCoreCLI@2
displayName: 'Publish MySqlConnector.DependencyInjection.Tests'
inputs:
command: 'publish'
- arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
+ arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
publishWebProjects: false
zipAfterPublish: false
- task: PublishPipelineArtifact@0
inputs:
- artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
- targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net8.0'
+ artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
+ targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net9.0'
- task: DotNetCoreCLI@2
- displayName: 'Publish IntegrationTests (7.0)'
+ displayName: 'Publish IntegrationTests (9.0)'
inputs:
command: 'publish'
- arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
+ arguments: '-c Release -f net9.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
publishWebProjects: false
zipAfterPublish: false
- task: PublishPipelineArtifact@0
inputs:
- artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
- targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
+ artifactName: 'IntegrationTests-net9.0-$(Agent.OS)'
+ targetPath: 'artifacts/publish/IntegrationTests/release_net9.0'
- task: DotNetCoreCLI@2
- displayName: 'Publish IntegrationTests (6.0)'
+ displayName: 'Publish IntegrationTests (8.0)'
inputs:
command: 'publish'
- arguments: '-c Release -f net6.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
+ arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
publishWebProjects: false
zipAfterPublish: false
- task: PublishPipelineArtifact@0
inputs:
- artifactName: 'IntegrationTests-net6.0-$(Agent.OS)'
- targetPath: 'artifacts/publish/IntegrationTests/release_net6.0'
+ artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
+ targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
diff --git a/.ci/conformance-test-steps.yml b/.ci/conformance-test-steps.yml
index 02d700623..349663215 100644
--- a/.ci/conformance-test-steps.yml
+++ b/.ci/conformance-test-steps.yml
@@ -10,14 +10,14 @@ steps:
- task: DownloadPipelineArtifact@0
condition: always()
inputs:
- artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
- targetPath: '$(Build.BinariesDirectory)/8.0'
+ artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
+ targetPath: '$(Build.BinariesDirectory)/9.0'
- task: DotNetCoreCLI@2
displayName: 'Conformance Tests'
inputs:
command: 'custom'
custom: 'vstest'
- arguments: '$(Build.BinariesDirectory)/8.0/Conformance.Tests.dll /logger:trx'
+ arguments: '$(Build.BinariesDirectory)/9.0/Conformance.Tests.dll /logger:trx'
env:
CONNECTION_STRING: ${{ parameters.connectionString }}
- task: PublishTestResults@2
diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index 14f8a0cc6..7d7bd5d08 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -8,9 +8,9 @@ steps:
- bash: ${{ format('.ci/docker-run.sh {0} 3300 {1}', parameters.image, parameters.unsupportedFeatures) }}
displayName: 'Start Docker container'
- task: UseDotNet@2
- displayName: 'Install .NET 6.0'
+ displayName: 'Install .NET 8.0'
inputs:
- version: 6.0.x
+ version: 8.0.x
packageType: runtime
- task: UseDotNet@2
displayName: 'Install .NET'
@@ -33,19 +33,19 @@ steps:
image: ${{ parameters.image }}
unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;UseCompression=True;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
- platform: 'net6.0'
+ platform: 'net8.0'
description: 'Compression'
- template: 'integration-test-steps.yml'
parameters:
image: ${{ parameters.image }}
unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
- platform: 'net6.0'
+ platform: 'net9.0'
description: 'No SSL'
- template: 'integration-test-steps.yml'
parameters:
image: ${{ parameters.image }}
unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
connectionString: server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600;certificate file=$(Build.Repository.LocalPath)/.ci/server/certs/ssl-client.pfx;${{ parameters.connectionStringExtra }}
- platform: 'net6.0'
+ platform: 'net9.0'
description: 'SSL'
diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml
index 012b11a0b..ef0e6a095 100644
--- a/.ci/mysqlconnector-tests-steps.yml
+++ b/.ci/mysqlconnector-tests-steps.yml
@@ -6,7 +6,7 @@ steps:
includePreviewVersions: true
- task: DownloadPipelineArtifact@0
inputs:
- artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
+ artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
targetPath: $(System.DefaultWorkingDirectory)
- task: DotNetCoreCLI@2
displayName: 'Run MySqlConnector.Tests'
@@ -16,7 +16,7 @@ steps:
arguments: 'MySqlConnector.Tests.dll /logger:trx'
- task: DownloadPipelineArtifact@0
inputs:
- artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
+ artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
targetPath: $(System.DefaultWorkingDirectory)
- task: DotNetCoreCLI@2
displayName: 'Run MySqlConnector.DependencyInjection.Tests'
@@ -28,4 +28,4 @@ steps:
inputs:
testResultsFormat: VSTest
testResultsFiles: '**/*.trx'
- testRunTitle: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
+ testRunTitle: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
diff --git a/.ci/test.ps1 b/.ci/test.ps1
index a619c6402..7d595e35e 100644
--- a/.ci/test.ps1
+++ b/.ci/test.ps1
@@ -38,14 +38,14 @@ dotnet test -c Release -f net462
if ($LASTEXITCODE -ne 0){
exit $LASTEXITCODE;
}
-dotnet test -c Release -f net7.0
+dotnet test -c Release -f net9.0
if ($LASTEXITCODE -ne 0){
exit $LASTEXITCODE;
}
echo "Executing integration tests with Compression, No SSL"
Copy-Item -Force ..\..\.ci\config\config.compression.json config.json
-dotnet test -c Release -f net6.0
+dotnet test -c Release -f net8.0
if ($LASTEXITCODE -ne 0){
exit $LASTEXITCODE;
}
diff --git a/appveyor.yml b/appveyor.yml
index c4f4f64c4..ef905dcaa 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,8 +8,8 @@ cache:
install:
- ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "install-dotnet.ps1"
- ps: .\install-dotnet.ps1 -Channel 6.0 -InstallDir "dotnetcli"
- - ps: .\install-dotnet.ps1 -Channel 7.0 -InstallDir "dotnetcli"
- ps: .\install-dotnet.ps1 -Channel 8.0 -InstallDir "dotnetcli"
+ - ps: .\install-dotnet.ps1 -Channel 9.0 -InstallDir "dotnetcli"
build_script:
- dotnet --info
before_test:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5e57f867e..fd74685be 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,5 +1,5 @@
variables:
- DotNetCoreSdkVersion: '8.x'
+ DotNetCoreSdkVersion: '9.x'
NUGET_PACKAGES: '$(Pipeline.Workspace)/.nuget/packages'
jobs:
@@ -16,17 +16,6 @@ jobs:
vmimage: 'windows-latest'
steps:
- template: '.ci/build-steps.yml'
- - task: DotNetCoreCLI@2
- displayName: 'Publish IntegrationTests (net472)'
- inputs:
- command: 'publish'
- arguments: '-c Release -f net472 tests/IntegrationTests/IntegrationTests.csproj'
- publishWebProjects: false
- zipAfterPublish: false
- - task: PublishPipelineArtifact@0
- inputs:
- artifactName: 'IntegrationTests-net472-$(Agent.OS)'
- targetPath: 'artifacts/publish/IntegrationTests/release_net472'
- job: windows_mysql_data
displayName: 'MySql.Data Tests'
@@ -41,9 +30,9 @@ jobs:
contents: 'config.json'
targetFolder: 'tests/IntegrationTests'
- task: UseDotNet@2
- displayName: 'Install .NET 7.0'
+ displayName: 'Install .NET 8.0'
inputs:
- version: '7.0.x'
+ version: '8.0.x'
- task: UseDotNet@2
displayName: 'Install .NET'
inputs:
@@ -91,11 +80,6 @@ jobs:
vmimage: 'windows-2019'
steps:
- template: '.ci/install-mysql-windows.yml'
- - task: UseDotNet@2
- displayName: 'Install .NET Core 3.1'
- inputs:
- version: 3.1.x
- packageType: runtime
- template: '.ci/mysqlconnector-tests-steps.yml'
- template: '.ci/conformance-test-steps.yml'
parameters:
@@ -123,18 +107,18 @@ jobs:
displayName: 'Remove target frameworks'
inputs:
targetType: 'inline'
- script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net472;net8.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+ script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net481;net9.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
- task: DotNetCoreCLI@2
displayName: 'Restore packages'
inputs:
command: 'restore'
- task: DotNetCoreCLI@2
- displayName: 'Integration tests (net472/net8.0)'
+ displayName: 'Integration tests (net481/net9.0)'
inputs:
command: 'test'
projects: 'tests/IntegrationTests/IntegrationTests.csproj'
- arguments: '-c Release --no-restore'
- testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
+ arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
+ testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
@@ -146,9 +130,9 @@ jobs:
steps:
- template: '.ci/install-mysql-windows.yml'
- task: UseDotNet@2
- displayName: 'Install .NET 6.0'
+ displayName: 'Install .NET 8.0'
inputs:
- version: 6.0.x
+ version: 8.0.x
packageType: runtime
- task: UseDotNet@2
displayName: 'Install .NET'
@@ -161,18 +145,18 @@ jobs:
displayName: 'Remove target frameworks'
inputs:
targetType: 'inline'
- script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net6.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+ script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net8.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
- task: DotNetCoreCLI@2
displayName: 'Restore packages'
inputs:
command: 'restore'
- task: DotNetCoreCLI@2
- displayName: 'Integration tests (net6.0)'
+ displayName: 'Integration tests (net8.0)'
inputs:
command: 'test'
projects: 'tests/IntegrationTests/IntegrationTests.csproj'
- arguments: '-c Release --no-restore'
- testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
+ arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
+ testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
diff --git a/global.json b/global.json
index 3a69170ca..746f46e86 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "8.0"
+ "version": "9.0"
}
}
diff --git a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
index 025362e0f..92fdf19c7 100644
--- a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
+++ b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
@@ -35,6 +35,10 @@ internal static bool TryGetPlugin(string name, [NotNullWhen(true)] out IAuthenti
return s_plugins.TryGetValue(name, out plugin);
}
+#if NET9_0_OR_GREATER
+ private static readonly Lock s_lock = new();
+#else
private static readonly object s_lock = new();
+#endif
private static readonly Dictionary s_plugins = [];
}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
index f8ff90a41..ff2b17fff 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -28,7 +28,9 @@ public override object ReadValue(ReadOnlySpan data, ColumnDefinitionPayloa
microseconds = -microseconds;
}
-#if NET7_0_OR_GREATER
+#if NET9_0_OR_GREATER
+ return TimeSpan.FromDays(days, hours, minutes, seconds, microseconds: microseconds);
+#elif NET7_0_OR_GREATER
return new TimeSpan(days, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
#else
return new TimeSpan(days, hours, minutes, seconds) + TimeSpan.FromTicks(microseconds * 10);
diff --git a/src/MySqlConnector/Core/ILoadBalancer.cs b/src/MySqlConnector/Core/ILoadBalancer.cs
index 8e3b106d8..48edbf47d 100644
--- a/src/MySqlConnector/Core/ILoadBalancer.cs
+++ b/src/MySqlConnector/Core/ILoadBalancer.cs
@@ -27,22 +27,38 @@ internal sealed class RandomLoadBalancer : ILoadBalancer
public IReadOnlyList LoadBalance(IReadOnlyList hosts)
{
#pragma warning disable CA5394 // Do not use insecure randomness
- // from https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
+#if NET8_0_OR_GREATER
+ var shuffled = hosts.ToArray();
+ lock (m_lock)
+ m_random.Shuffle(shuffled);
+ return shuffled;
+#else
var shuffled = new List(hosts);
+ // from https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
for (var i = hosts.Count - 1; i >= 1; i--)
{
int j;
- lock (m_random)
+ lock (m_lock)
j = m_random.Next(i + 1);
if (i != j)
(shuffled[j], shuffled[i]) = (shuffled[i], shuffled[j]);
}
return shuffled;
+#endif
}
- private RandomLoadBalancer() => m_random = new();
+ private RandomLoadBalancer()
+ {
+ m_random = new();
+ m_lock = new();
+ }
private readonly Random m_random;
+#if NET9_0_OR_GREATER
+ private readonly Lock m_lock;
+#else
+ private readonly object m_lock;
+#endif
}
internal sealed class RoundRobinLoadBalancer : ILoadBalancer
@@ -63,6 +79,10 @@ public IReadOnlyList LoadBalance(IReadOnlyList hosts)
return shuffled;
}
+#if NET9_0_OR_GREATER
+ private readonly Lock m_lock;
+#else
private readonly object m_lock;
+#endif
private uint m_counter;
}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 93112e16f..d6d613ed6 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1471,7 +1471,11 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
{
try
{
+#if NET9_0_OR_GREATER
+ var certificate = X509CertificateLoader.LoadPkcs12FromFile(cs.CertificateFile, cs.CertificatePassword, X509KeyStorageFlags.MachineKeySet);
+#else
var certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword, X509KeyStorageFlags.MachineKeySet);
+#endif
if (!certificate.HasPrivateKey)
{
certificate.Dispose();
@@ -1543,7 +1547,9 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
{
// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
Log.LoadingCaCertificate(m_logger, Id, index);
-#if NET5_0_OR_GREATER
+#if NET9_0_OR_GREATER
+ var caCertificate = X509CertificateLoader.LoadCertificate(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index));
+#elif NET5_0_OR_GREATER
var caCertificate = new X509Certificate2(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(ReadOnlySpan), X509KeyStorageFlags.MachineKeySet);
#else
var caCertificate = new X509Certificate2(Utility.ArraySlice(certificateBytes, index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(string), X509KeyStorageFlags.MachineKeySet);
@@ -1739,7 +1745,11 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
// Schannel has a bug where ephemeral keys can't be loaded: https://github.com/dotnet/runtime/issues/23749#issuecomment-485947319
// The workaround is to export the key (which may make it "Perphemeral"): https://github.com/dotnet/runtime/issues/23749#issuecomment-739895373
var oldCertificate = m_clientCertificate;
+#if NET9_0_OR_GREATER
+ m_clientCertificate = X509CertificateLoader.LoadPkcs12(m_clientCertificate.Export(X509ContentType.Pkcs12, default(string?)), null);
+#else
m_clientCertificate = new X509Certificate2(m_clientCertificate.Export(X509ContentType.Pkcs12));
+#endif
oldCertificate.Dispose();
}
return [m_clientCertificate];
@@ -1810,7 +1820,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
throw new MySqlException("Could not load the client key from " + sslCertificateFile, ex);
}
#endif
- }
+ }
}
#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
@@ -1867,15 +1877,12 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
// first (and only) row
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
- static void ReadRow(ReadOnlySpan span, out int? connectionId, out ServerVersion? serverVersion)
- {
- var reader = new ByteArrayReader(span);
- var length = reader.ReadLengthEncodedIntegerOrNull();
- connectionId = (length != -1 && Utf8Parser.TryParse(reader.ReadByteString(length), out int id, out _)) ? id : default(int?);
- length = reader.ReadLengthEncodedIntegerOrNull();
- serverVersion = length != -1 ? new ServerVersion(reader.ReadByteString(length)) : default;
- }
- ReadRow(payload.Span, out var connectionId, out var serverVersion);
+
+ var reader = new ByteArrayReader(payload.Span);
+ var length = reader.ReadLengthEncodedIntegerOrNull();
+ var connectionId = (length != -1 && Utf8Parser.TryParse(reader.ReadByteString(length), out int id, out _)) ? id : default(int?);
+ length = reader.ReadLengthEncodedIntegerOrNull();
+ var serverVersion = length != -1 ? new ServerVersion(reader.ReadByteString(length)) : default;
// OK/EOF payload
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 07f3bbb41..8b2fc8306 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -176,8 +176,7 @@ internal async ValueTask LoadAsync(IOBehavior ioBehavior, CancellationToken
{
// replace the file name with a sentinel so that we know (when processing the result set) that it's not spoofed by the server
var newFileName = GenerateSourceFileName();
- lock (s_lock)
- s_sources.Add(newFileName, CreateFileStream(FileName!));
+ AddSource(newFileName, CreateFileStream(FileName!));
FileName = newFileName;
}
}
@@ -187,8 +186,7 @@ internal async ValueTask LoadAsync(IOBehavior ioBehavior, CancellationToken
throw new InvalidOperationException("Local must be true to use SourceStream, SourceDataTable, or SourceDataReader.");
FileName = GenerateSourceFileName();
- lock (s_lock)
- s_sources.Add(FileName, Source!);
+ AddSource(FileName, Source!);
}
var closeConnection = false;
@@ -221,6 +219,12 @@ internal async ValueTask LoadAsync(IOBehavior ioBehavior, CancellationToken
if (closeConnection)
Connection.Close();
}
+
+ static void AddSource(string name, object source)
+ {
+ lock (s_lock)
+ s_sources.Add(name, source);
+ }
}
internal const string SourcePrefix = ":SOURCE:";
@@ -316,6 +320,10 @@ internal static bool TryGetAndRemoveSource(string sourceKey, [NotNullWhen(true)]
private static string GenerateSourceFileName() => SourcePrefix + Guid.NewGuid().ToString("N");
+#if NET9_0_OR_GREATER
+ private static readonly Lock s_lock = new();
+#else
private static readonly object s_lock = new();
+#endif
private static readonly Dictionary s_sources = [];
}
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index b98c330f7..a037a3eed 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1265,7 +1265,11 @@ private ConnectionSettings GetConnectionSettings() =>
private static readonly StateChangeEventArgs s_stateChangeClosedConnecting = new(ConnectionState.Closed, ConnectionState.Connecting);
private static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new(ConnectionState.Connecting, ConnectionState.Open);
private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed);
+#if NET9_0_OR_GREATER
+ private static readonly Lock s_lock = new();
+#else
private static readonly object s_lock = new();
+#endif
private static readonly Dictionary> s_transactionConnections = [];
private static readonly ReadOnlyMemory[] s_startTransactionPayloads = new ReadOnlyMemory[5 * 3 * 2];
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index db3ec187d..769a9a5e6 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
- net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0
+ net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0
A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.
Copyright 2016–2024 Bradley Grainger
Bradley Grainger
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 77e20d081..259f3f963 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -1,6 +1,5 @@
using System.Buffers;
using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Utilities;
namespace MySqlConnector.Protocol.Serialization;
diff --git a/src/MySqlConnector/Utilities/TimerQueue.cs b/src/MySqlConnector/Utilities/TimerQueue.cs
index 5aa05f736..3b120264e 100644
--- a/src/MySqlConnector/Utilities/TimerQueue.cs
+++ b/src/MySqlConnector/Utilities/TimerQueue.cs
@@ -131,7 +131,11 @@ public Data(uint id, int time, Action action)
public Action Action { get; }
}
+#if NET9_0_OR_GREATER
+ private readonly Lock m_lock;
+#else
private readonly object m_lock;
+#endif
private readonly Timer m_timer;
private readonly List m_timeoutActions;
private uint m_counter;
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 63f07c8c6..69f23a5a9 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -430,7 +430,9 @@ public static TimeSpan ParseTimeSpan(ReadOnlySpan value)
seconds = -seconds;
microseconds = -microseconds;
}
-#if NET7_0_OR_GREATER
+#if NET9_0_OR_GREATER
+ return TimeSpan.FromHours(hours, minutes, seconds, microseconds: microseconds);
+#elif NET7_0_OR_GREATER
return new TimeSpan(0, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
#else
return new TimeSpan(0, hours, minutes, seconds, microseconds / 1000) + TimeSpan.FromTicks(microseconds % 1000 * 10);
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index 246efa5b1..e2a4ca928 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
0.1.0
true
true
diff --git a/tests/IntegrationTests/ClientFactoryTests.cs b/tests/IntegrationTests/ClientFactoryTests.cs
index bebd2f5e0..f74017a73 100644
--- a/tests/IntegrationTests/ClientFactoryTests.cs
+++ b/tests/IntegrationTests/ClientFactoryTests.cs
@@ -51,7 +51,7 @@ public void DbProviderFactoriesGetFactory()
#else
var providerInvariantName = "MySqlConnector";
#endif
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
DbProviderFactories.RegisterFactory(providerInvariantName, MySqlConnectorFactory.Instance);
#endif
var factory = DbProviderFactories.GetFactory(providerInvariantName);
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 55360d60c..b68d6a05a 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,12 +1,12 @@
- net462;net472;net6.0;net8.0
+ net462;net481;net6.0;net8.0;net9.0
false
- net8.0
+ net9.0
MYSQL_DATA
$(NoWarn);MSB3246
@@ -61,7 +61,7 @@
-
+
diff --git a/tests/IntegrationTests/RedirectionTests.cs b/tests/IntegrationTests/RedirectionTests.cs
index 892f1eb7f..f5f374eb9 100644
--- a/tests/IntegrationTests/RedirectionTests.cs
+++ b/tests/IntegrationTests/RedirectionTests.cs
@@ -1,5 +1,4 @@
#if !MYSQL_DATA
-using System.Globalization;
using System.Net;
using System.Net.Sockets;
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index 6c428f386..e33d33917 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -64,7 +64,11 @@ public async Task ConnectSslClientCertificateCallback(string certificateFile, st
using var connection = new MySqlConnection(csb.ConnectionString);
connection.ProvideClientCertificatesCallback = x =>
{
+#if NET9_0_OR_GREATER
+ x.Add(X509CertificateLoader.LoadPkcs12FromFile(certificateFilePath, certificateFilePassword));
+#else
x.Add(new X509Certificate2(certificateFilePath, certificateFilePassword));
+#endif
return default;
};
@@ -103,7 +107,9 @@ private async Task DoTestSsl(string connectionString)
Assert.True(connection.SslIsEncrypted);
Assert.True(connection.SslIsSigned);
Assert.True(connection.SslIsAuthenticated);
+#if !NET9_0_OR_GREATER
Assert.True(connection.SslIsMutuallyAuthenticated);
+#endif
#endif
cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
var sslVersion = (string) await cmd.ExecuteScalarAsync();
@@ -117,7 +123,11 @@ public async Task ConnectSslClientCertificateFromCertificateStore(string certFil
// Create a mock of certificate store
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
+#if NET9_0_OR_GREATER
+ var certificate = X509CertificateLoader.LoadPkcs12FromFile(Path.Combine(AppConfig.CertsPath, certFile), null);
+#else
var certificate = new X509Certificate2(Path.Combine(AppConfig.CertsPath, certFile));
+#endif
store.Add(certificate);
var csb = AppConfig.CreateConnectionStringBuilder();
@@ -133,9 +143,11 @@ public async Task ConnectSslClientCertificateFromCertificateStore(string certFil
Assert.True(connection.SslIsEncrypted);
Assert.True(connection.SslIsSigned);
Assert.True(connection.SslIsAuthenticated);
+#if !NET9_0_OR_GREATER
Assert.True(connection.SslIsMutuallyAuthenticated);
#endif
- cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
+#endif
+ cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
var sslVersion = (string) await cmd.ExecuteScalarAsync();
Assert.False(string.IsNullOrWhiteSpace(sslVersion));
}
diff --git a/tests/IntegrationTests/Transaction.cs b/tests/IntegrationTests/Transaction.cs
index 7a22b96f5..ac33ab5e0 100644
--- a/tests/IntegrationTests/Transaction.cs
+++ b/tests/IntegrationTests/Transaction.cs
@@ -169,7 +169,7 @@ public async Task ReadWriteTransactionAsync()
Assert.Equal(new[] { 1, 2 }, results);
}
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
[Fact]
public async Task DbConnectionCommitAsync()
{
@@ -241,7 +241,7 @@ public async Task RollbackDisposeAsync()
Assert.Equal(new int[0], results);
}
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
[Fact]
public async Task DbConnectionRollbackAsync()
{
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
index fac726a3a..ebeeef845 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
+++ b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
true
true
..\..\MySqlConnector.snk
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index b7c7f164e..5d14a2cce 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -1,7 +1,7 @@
- net481;net8.0
+ net481;net9.0
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
index 73e62aa5e..85c1ed799 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net9.0
enable
enable
true