diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml index 95cfc5553..c95f7acf3 100644 --- a/.ci/build-steps.yml +++ b/.ci/build-steps.yml @@ -6,9 +6,10 @@ steps: contents: 'config.json' targetFolder: 'tests/IntegrationTests' - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - task: DotNetCoreCLI@2 displayName: 'Restore packages' @@ -26,37 +27,37 @@ steps: displayName: 'Publish MySqlConnector.Tests' inputs: command: 'publish' - arguments: '-c Release -f net7.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj' + arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj' publishWebProjects: false zipAfterPublish: false - task: PublishPipelineArtifact@0 inputs: - artifactName: 'MySqlConnector.Tests-7.0-$(Agent.OS)' - targetPath: 'tests/MySqlConnector.Tests/bin/Release/net7.0/publish' + artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)' + targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net8.0' - task: DotNetCoreCLI@2 displayName: 'Publish Conformance.Tests' inputs: command: 'publish' - arguments: '-c Release -f net7.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj' + arguments: '-c Release -f net8.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj' publishWebProjects: false zipAfterPublish: false - task: PublishPipelineArtifact@0 inputs: - artifactName: 'Conformance.Tests-7.0-$(Agent.OS)' - targetPath: 'tests/Conformance.Tests/bin/Release/net7.0/publish' + artifactName: 'Conformance.Tests-8.0-$(Agent.OS)' + targetPath: 'artifacts/publish/Conformance.Tests/release_net8.0' - task: DotNetCoreCLI@2 displayName: 'Publish IntegrationTests (7.0)' inputs: command: 'publish' - arguments: '-c Release -f net7.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-net7.0-$(Agent.OS)' - targetPath: 'tests/IntegrationTests/bin/Release/net7.0/publish' + artifactName: 'IntegrationTests-net8.0-$(Agent.OS)' + targetPath: 'artifacts/publish/IntegrationTests/release_net8.0' - task: DotNetCoreCLI@2 displayName: 'Publish IntegrationTests (6.0)' @@ -68,4 +69,4 @@ steps: - task: PublishPipelineArtifact@0 inputs: artifactName: 'IntegrationTests-net6.0-$(Agent.OS)' - targetPath: 'tests/IntegrationTests/bin/Release/net6.0/publish' + targetPath: 'artifacts/publish/IntegrationTests/release_net6.0' diff --git a/.ci/config/config.compression+ssl.json b/.ci/config/config.compression+ssl.json index 206c84969..8a11c95bf 100644 --- a/.ci/config/config.compression+ssl.json +++ b/.ci/config/config.compression+ssl.json @@ -1,12 +1,12 @@ { "Data": { "ConnectionString": "server=127.0.0.1;user id=ssltest;password=test;port=3306;database=mysqltest;ssl mode=required;use compression=true;DefaultCommandTimeout=3600", - "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock", + "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV", - "CertificatesPath": "../../../../../.ci/server/certs" + "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV", + "CertificatesPath": "../../../../.ci/server/certs" } } diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json index afd6f6bee..ca3199da0 100644 --- a/.ci/config/config.compression.json +++ b/.ci/config/config.compression.json @@ -1,11 +1,11 @@ { "Data": { "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;UseCompression=true;DefaultCommandTimeout=3600", - "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock", + "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV" + "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV" } } diff --git a/.ci/config/config.json b/.ci/config/config.json index b0332563d..fa5908d80 100644 --- a/.ci/config/config.json +++ b/.ci/config/config.json @@ -1,11 +1,11 @@ { "Data": { "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600", - "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock", + "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV" + "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV" } } diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json index d2132e290..ef4a5afb0 100644 --- a/.ci/config/config.ssl.json +++ b/.ci/config/config.ssl.json @@ -1,12 +1,12 @@ { "Data": { "ConnectionString": "server=127.0.0.1;user id=ssltest;password=test;port=3306;database=mysqltest;ssl mode=required;certificate file=../../../../../.ci/server/certs/ssl-client.pfx;DefaultCommandTimeout=3600", - "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock", + "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV", - "CertificatesPath": "../../../../../.ci/server/certs" + "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV", + "CertificatesPath": "../../../../.ci/server/certs" } } diff --git a/.ci/config/config.uds+ssl.json b/.ci/config/config.uds+ssl.json index 26d67f019..eb754da59 100644 --- a/.ci/config/config.uds+ssl.json +++ b/.ci/config/config.uds+ssl.json @@ -1,11 +1,11 @@ { "Data": { - "ConnectionString": "server=./../../../../../.ci/mysqld/mysqld.sock;user id=ssltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600", + "ConnectionString": "server=./../../../../.ci/mysqld/mysqld.sock;user id=ssltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "None", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV", - "CertificatesPath": "../../../../../.ci/server/certs" + "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV", + "CertificatesPath": "../../../../.ci/server/certs" } } diff --git a/.ci/config/config.uds.json b/.ci/config/config.uds.json index d3e90c036..6b209d734 100644 --- a/.ci/config/config.uds.json +++ b/.ci/config/config.uds.json @@ -1,10 +1,10 @@ { "Data": { - "ConnectionString": "server=./../../../../../.ci/run/mysql/mysqld.sock;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600", + "ConnectionString": "server=./../../../../.ci/run/mysql/mysqld.sock;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600", "PasswordlessUser": "no_password", "SecondaryDatabase": "testdb2", "UnsupportedFeatures": "None", - "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV", - "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV" + "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV", + "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV" } } diff --git a/.ci/conformance-test-steps.yml b/.ci/conformance-test-steps.yml index 28d38cbc0..02d700623 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-7.0-$(Agent.OS)' - targetPath: '$(Build.BinariesDirectory)/7.0' + artifactName: 'Conformance.Tests-8.0-$(Agent.OS)' + targetPath: '$(Build.BinariesDirectory)/8.0' - task: DotNetCoreCLI@2 displayName: 'Conformance Tests' inputs: command: 'custom' custom: 'vstest' - arguments: '$(Build.BinariesDirectory)/7.0/Conformance.Tests.dll /logger:trx' + arguments: '$(Build.BinariesDirectory)/8.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 4e89b919c..d56e29c11 100644 --- a/.ci/integration-tests-steps.yml +++ b/.ci/integration-tests-steps.yml @@ -8,19 +8,15 @@ steps: - bash: ${{ format('.ci/docker-run.sh {0} 3300 {1}', parameters.image, parameters.unsupportedFeatures) }} displayName: 'Start Docker container' - task: UseDotNet@2 - displayName: 'Install .NET Core 3.1' - inputs: - version: 3.1.x - packageType: runtime -- task: UseDotNet@2 - displayName: 'Install .NET Core 6.0' + displayName: 'Install .NET 6.0' inputs: version: 6.0.x packageType: runtime - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - template: 'conformance-test-steps.yml' parameters: image: ${{ parameters.image }} @@ -30,7 +26,7 @@ steps: 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: 'net7.0' + platform: 'net8.0' description: 'SSL' - template: 'integration-test-steps.yml' parameters: @@ -52,4 +48,4 @@ steps: 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' - description: 'SSL' \ No newline at end of file + description: 'SSL' diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml index 6e25b9f90..ca4b1ce8f 100644 --- a/.ci/mysqlconnector-tests-steps.yml +++ b/.ci/mysqlconnector-tests-steps.yml @@ -1,11 +1,12 @@ steps: - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - task: DownloadPipelineArtifact@0 inputs: - artifactName: 'MySqlConnector.Tests-7.0-$(Agent.OS)' + artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)' targetPath: $(System.DefaultWorkingDirectory) - task: DotNetCoreCLI@2 displayName: 'Run MySqlConnector.Tests' @@ -17,4 +18,4 @@ steps: inputs: testResultsFormat: VSTest testResultsFiles: '**/*.trx' - testRunTitle: 'MySqlConnector.Tests-7.0-$(Agent.OS)' \ No newline at end of file + testRunTitle: 'MySqlConnector.Tests-8.0-$(Agent.OS)' diff --git a/.editorconfig b/.editorconfig index 6d1ccd5a7..1765a3ee5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -79,7 +79,6 @@ dotnet_diagnostic.CA1508.severity = silent # Avoid dead conditional code. dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException.ThrowIfNull. dotnet_diagnostic.CA1513.severity = none # Use ObjectDisposedException.ThrowIf. dotnet_diagnostic.CA1849.severity = none # Call async methods when in an async method. -dotnet_diagnostic.CA1859.severity = silent # Use concrete return types for performance. dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern. dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities. dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed. @@ -111,6 +110,7 @@ dotnet_diagnostic.SA1500.severity = none # Braces for multi-line statements shou dotnet_diagnostic.SA1503.severity = none # Braces should not be omitted. dotnet_diagnostic.SA1513.severity = none # Closing brace should be followed by blank line. dotnet_diagnostic.SA1516.severity = none # Elements should be separated by blank line. +dotnet_diagnostic.SA1519.severity = none # Braces should not be omitted from multi-line child statement dotnet_diagnostic.SA1600.severity = none # Elements should be documented. dotnet_diagnostic.SA1601.severity = none # Partial elements should be documented. dotnet_diagnostic.SA1602.severity = none # Enumeration items should be documented. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c60d67252..0d9342836 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,10 +15,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up .NET 7.0 + - name: Set up .NET 8.0 uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x + dotnet-version: 8.x - name: Restore run: dotnet restore diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4a4d082d3..d9c51efda 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -26,10 +26,10 @@ jobs: - name: Check out uses: actions/checkout@v4 - - name: Set up .NET 7.0 + - name: Set up .NET 8.0 uses: actions/setup-dotnet@v3 with: - dotnet-version: 7.x + dotnet-version: 8.x - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.gitignore b/.gitignore index b58dd54e1..e72e818f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # ignore output folders BenchmarkDotNet.Artifacts/ +artifacts/ bin/ build/ obj/ diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..c475ff91a --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,22 @@ + + + + true + preview + true + $(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039 + $(MSBuildThisFileDirectory)artifacts + true + low + + + + true + + + + true + true + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000..a874fb181 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,34 @@ + + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NuGet.config b/NuGet.config index 6873eb959..4d736c19e 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,6 +1,7 @@ + diff --git a/appveyor.yml b/appveyor.yml index 001c7edfe..1f30f422b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,6 +11,7 @@ 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" build_script: - dotnet --info before_test: @@ -21,19 +22,13 @@ test_script: - ps: .\.ci\test.ps1 after_test: - cmd: |- - dotnet pack src\MySqlConnector\MySqlConnector.csproj -c Release - dotnet pack src\MySqlConnector.Authentication.Ed25519\MySqlConnector.Authentication.Ed25519.csproj -c Release - dotnet pack src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj -c Release - dotnet pack src\MySqlConnector.Logging.log4net\MySqlConnector.Logging.log4net.csproj -c Release - dotnet pack src\MySqlConnector.Logging.Microsoft.Extensions.Logging\MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj -c Release - dotnet pack src\MySqlConnector.Logging.NLog\MySqlConnector.Logging.NLog.csproj -c Release - dotnet pack src\MySqlConnector.Logging.Serilog\MySqlConnector.Logging.Serilog.csproj -c Release + dotnet pack -c Release notifications: - provider: Slack incoming_webhook: secure: SRJ5fYQE4tvelyqeX3Lkv0gXta3O2pl4/+wNaqmqmcFkmYBe+U31T5YCGhipBOVhHBIZLevihOJZ78sFVJrmAFV5bHgtX/VPIKEXN+7ytis= artifacts: -- path: src\*\bin\Release\*.nupkg +- path: artifacts\package\release\*.nupkg deploy: - provider: NuGet api_key: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 91efcf8e0..43cda8c76 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,5 @@ variables: - DotNetCoreSdkVersion: '7.x' + DotNetCoreSdkVersion: '8.x' NUGET_PACKAGES: '$(Pipeline.Workspace)/.nuget/packages' jobs: @@ -26,7 +26,7 @@ jobs: - task: PublishPipelineArtifact@0 inputs: artifactName: 'IntegrationTests-net472-$(Agent.OS)' - targetPath: 'tests/IntegrationTests/bin/Release/net472/publish' + targetPath: 'artifacts/publish/IntegrationTests/release_net472' - job: windows_mysql_data displayName: 'MySql.Data Tests' @@ -41,13 +41,14 @@ jobs: contents: 'config.json' targetFolder: 'tests/IntegrationTests' - task: UseDotNet@2 - displayName: 'Install .NET Core 7.0' + displayName: 'Install .NET 7.0' inputs: version: '7.0.x' - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - task: DotNetCoreCLI@2 displayName: 'MySql.Data unit tests' inputs: @@ -108,9 +109,10 @@ jobs: steps: - template: '.ci/install-mysql-windows.yml' - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - task: CopyFiles@2 displayName: 'Copy config.json' inputs: @@ -121,18 +123,18 @@ jobs: displayName: 'Remove target frameworks' inputs: targetType: 'inline' - script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net472;net7.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj' + script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''.*'', ''net472;net8.0'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj' - task: DotNetCoreCLI@2 displayName: 'Restore packages' inputs: command: 'restore' - task: DotNetCoreCLI@2 - displayName: 'Integration tests (net472/net7.0)' + displayName: 'Integration tests (net472/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', 'net472/net7.0', 'No SSL') }} + testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }} env: DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket' DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True' @@ -144,14 +146,15 @@ jobs: steps: - template: '.ci/install-mysql-windows.yml' - task: UseDotNet@2 - displayName: 'Install .NET Core 6.0' + displayName: 'Install .NET 6.0' inputs: version: 6.0.x packageType: runtime - task: UseDotNet@2 - displayName: 'Install .NET Core' + displayName: 'Install .NET' inputs: version: $(DotNetCoreSdkVersion) + includePreviewVersions: true - script: copy .ci\config\config.json tests\IntegrationTests\config.json displayName: 'Copy config.json' - task: PowerShell@2 diff --git a/global.json b/global.json index 3e3cfd9ca..7a8f3d994 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,6 @@ { "sdk": { - "version": "7.0.402" + "version": "8.0", + "allowPrerelease": true } } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e73c5bfa3..0968ec93d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,10 +1,10 @@ + true true ..\..\MySqlConnector.snk - true https://mysqlconnector.net/overview/version-history/ https://mysqlconnector.net/ MIT @@ -17,8 +17,6 @@ 2.0.0 true true - 11.0 - true latest-all false true @@ -26,33 +24,18 @@ true true true - $(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039 beta.0 - - true - - - - true - true - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml index a95918371..738dbc799 100644 --- a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml +++ b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + PKV006 diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj index 5e1488102..d1e793b74 100644 --- a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj +++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj @@ -14,7 +14,7 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj index 5233621fb..92ba235e8 100644 --- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj +++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml index a95918371..738dbc799 100644 --- a/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml +++ b/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + PKV006 diff --git a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj index 343e2fc58..b0cb9e194 100644 --- a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj +++ b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml index a95918371..738dbc799 100644 --- a/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml +++ b/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + PKV006 diff --git a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj index b3344f37d..316d42263 100644 --- a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj +++ b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml index a95918371..738dbc799 100644 --- a/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml +++ b/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml @@ -1,4 +1,5 @@  + PKV006 diff --git a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj index 3e25ac19c..6e655a9e4 100644 --- a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj +++ b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj @@ -12,7 +12,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs index 0b1c1246c..025362e0f 100644 --- a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs +++ b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs @@ -13,11 +13,18 @@ public static class AuthenticationPlugins /// The authentication plugin. public static void Register(IAuthenticationPlugin plugin) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(plugin); +#else if (plugin is null) throw new ArgumentNullException(nameof(plugin)); +#endif +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(plugin.Name); +#else if (string.IsNullOrEmpty(plugin.Name)) throw new ArgumentException("Invalid plugin name.", nameof(plugin)); - +#endif lock (s_lock) s_plugins.Add(plugin.Name, plugin); } @@ -29,5 +36,5 @@ internal static bool TryGetPlugin(string name, [NotNullWhen(true)] out IAuthenti } private static readonly object s_lock = new(); - private static readonly Dictionary s_plugins = new(); + private static readonly Dictionary s_plugins = []; } diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs index 8cc2dd94f..0f4df3ff6 100644 --- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs +++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs @@ -15,7 +15,9 @@ public override object ReadValue(ReadOnlySpan data, ColumnDefinitionPayloa #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guid ReadGuid(ReadOnlySpan data) => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +#if NET8_0_OR_GREATER + new(data, bigEndian: true); +#elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER new(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] }); #else new(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] }); diff --git a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs index 0bd7c2996..99e9fd96f 100644 --- a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs +++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs @@ -12,9 +12,11 @@ public override object ReadValue(ReadOnlySpan data, ColumnDefinitionPayloa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Guid ReadGuid(ReadOnlySpan data) => -#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - new Guid(data); +#if NET8_0_OR_GREATER + new(data, bigEndian: false); +#elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + new(data); #else - new Guid(data.ToArray()); + new(data.ToArray()); #endif } diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml index 35851e7b5..07cb181f8 100644 --- a/src/MySqlConnector/CompatibilitySuppressions.xml +++ b/src/MySqlConnector/CompatibilitySuppressions.xml @@ -20,6 +20,132 @@ lib/net6.0/MySqlConnector.dll lib/net7.0/MySqlConnector.dll + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Collections.Generic.IEnumerable{System.Data.DataRow},System.Int32,System.Threading.CancellationToken) + lib/net461/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.DataTable,System.Threading.CancellationToken) + lib/net461/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.IDataReader,System.Threading.CancellationToken) + lib/net461/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback + lib/net461/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken) + lib/net461/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Collections.Generic.IEnumerable{System.Data.DataRow},System.Int32,System.Threading.CancellationToken) + lib/net471/MySqlConnector.dll + lib/net471/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.DataTable,System.Threading.CancellationToken) + lib/net471/MySqlConnector.dll + lib/net471/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.IDataReader,System.Threading.CancellationToken) + lib/net471/MySqlConnector.dll + lib/net471/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback + lib/net471/MySqlConnector.dll + lib/net471/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken) + lib/net471/MySqlConnector.dll + lib/net471/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken) + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.0/MySqlConnector.dll + true + + + CP0002 + M:MySqlConnector.MySqlCommand.DisposeAsync + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.1/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlConnection.DisposeAsync + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.1/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlDataReader.DisposeAsync + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.1/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlTransaction.DisposeAsync + lib/netstandard2.0/MySqlConnector.dll + lib/netstandard2.1/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlBatchCommandCollection.GetEnumerator + lib/netstandard2.1/MySqlConnector.dll + lib/net6.0/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlConnectorFactory.CreateBatch + lib/netstandard2.1/MySqlConnector.dll + lib/net6.0/MySqlConnector.dll + + + CP0002 + M:MySqlConnector.MySqlConnectorFactory.CreateBatchCommand + lib/netstandard2.1/MySqlConnector.dll + lib/net6.0/MySqlConnector.dll + CP0008 T:MySqlConnector.MySqlDataReader diff --git a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs b/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs index 3b28ebe35..c17c53335 100644 --- a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs +++ b/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs @@ -13,7 +13,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict writer.Write((byte) CommandKind.Multi); bool? firstResult = default; bool wroteCommand; - ReadOnlySpan padding = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ReadOnlySpan padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; do { // save room for command length diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs index 26a45264c..c0f788649 100644 --- a/src/MySqlConnector/Core/CachedProcedure.cs +++ b/src/MySqlConnector/Core/CachedProcedure.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using System.Text.RegularExpressions; @@ -146,7 +147,7 @@ internal static List ParseParameters(string parametersSql) parametersSql = s_multipleSpaces.Replace(parametersSql, " "); if (string.IsNullOrWhiteSpace(parametersSql)) - return new List(); + return []; // strip precision specifier containing comma parametersSql = s_numericTypes.Replace(parametersSql, @"$1"); @@ -233,6 +234,7 @@ private static CachedParameter CreateCachedParameter(int ordinal, string? direct private string FullyQualified => $"`{m_schema}`.`{m_component}`"; + [SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Avoid mutable static field")] private static readonly IReadOnlyDictionary s_typeMapping = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "BOOL", "TINYINT" }, diff --git a/src/MySqlConnector/Core/ColumnTypeMetadata.cs b/src/MySqlConnector/Core/ColumnTypeMetadata.cs index 48e4a4b1b..eadb27de4 100644 --- a/src/MySqlConnector/Core/ColumnTypeMetadata.cs +++ b/src/MySqlConnector/Core/ColumnTypeMetadata.cs @@ -1,31 +1,18 @@ namespace MySqlConnector.Core; -internal sealed class ColumnTypeMetadata +internal sealed class ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0) { public static string CreateLookupKey(string columnTypeName, bool isUnsigned, int length) => $"{columnTypeName}|{(isUnsigned ? "u" : "s")}|{length}"; - public ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0) - { - DataTypeName = dataTypeName; - SimpleDataTypeName = simpleDataTypeName ?? dataTypeName; - CreateFormat = createFormat ?? (dataTypeName + (isUnsigned ? " UNSIGNED" : "")); - DbTypeMapping = dbTypeMapping; - MySqlDbType = mySqlDbType; - ColumnSize = columnSize; - IsUnsigned = isUnsigned; - Binary = binary; - Length = length; - } - - public string DataTypeName { get; } - public string SimpleDataTypeName { get; } - public string CreateFormat { get; } - public DbTypeMapping DbTypeMapping { get; } - public MySqlDbType MySqlDbType { get; } - public bool Binary { get; } - public long ColumnSize { get; } - public bool IsUnsigned { get; } - public int Length { get; } + public string DataTypeName { get; } = dataTypeName; + public string SimpleDataTypeName { get; } = simpleDataTypeName ?? dataTypeName; + public string CreateFormat { get; } = createFormat ?? (dataTypeName + (isUnsigned ? " UNSIGNED" : "")); + public DbTypeMapping DbTypeMapping { get; } = dbTypeMapping; + public MySqlDbType MySqlDbType { get; } = mySqlDbType; + public bool Binary { get; } = binary; + public long ColumnSize { get; } = columnSize; + public bool IsUnsigned { get; } = isUnsigned; + public int Length { get; } = length; public string CreateLookupKey() => CreateLookupKey(DataTypeName, IsUnsigned, Length); } diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs index 74b2b4e6a..a2581334a 100644 --- a/src/MySqlConnector/Core/CommandExecutor.cs +++ b/src/MySqlConnector/Core/CommandExecutor.cs @@ -26,7 +26,7 @@ public static async ValueTask ExecuteReaderAsync(CommandListPos var command2 = commandListPosition.CommandAt(commandIndex); if (command2.CommandType == CommandType.StoredProcedure) { - cachedProcedures ??= new(); + cachedProcedures ??= []; var commandText = command2.CommandText!; if (!cachedProcedures.ContainsKey(commandText)) { diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs index 8544caeee..ad4f917f9 100644 --- a/src/MySqlConnector/Core/ConnectionPool.cs +++ b/src/MySqlConnector/Core/ConnectionPool.cs @@ -555,18 +555,18 @@ public static async Task ClearPoolsAsync(IOBehavior ioBehavior, CancellationToke { foreach (var pool in GetAllPools()) await pool.ClearAsync(ioBehavior, cancellationToken).ConfigureAwait(false); - } - private static IReadOnlyList GetAllPools() - { - var pools = new List(s_pools.Count); - var uniquePools = new HashSet(); - foreach (var pool in s_pools.Values) + static List GetAllPools() { - if (pool is not null && uniquePools.Add(pool)) - pools.Add(pool); + var pools = new List(s_pools.Count); + var uniquePools = new HashSet(); + foreach (var pool in s_pools.Values) + { + if (pool is not null && uniquePools.Add(pool)) + pools.Add(pool); + } + return pools; } - return pools; } private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs, string? name) @@ -580,10 +580,10 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, m_cleanSemaphore = new(1); m_sessionSemaphore = new(cs.MaximumPoolSize); m_sessions = new(); - m_leasedSessions = new(); + m_leasedSessions = []; if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.LoadBalance == MySqlLoadBalance.LeastConnections) { - m_hostSessions = new(); + m_hostSessions = []; foreach (var hostName in cs.HostNames!) m_hostSessions[hostName] = 0; } @@ -741,29 +741,19 @@ private void AdjustHostConnectionCount(ServerSession session, int delta) } } - private sealed class LeastConnectionsLoadBalancer : ILoadBalancer + private sealed class LeastConnectionsLoadBalancer(Dictionary hostSessions) : ILoadBalancer { - public LeastConnectionsLoadBalancer(Dictionary hostSessions) => m_hostSessions = hostSessions; - public IReadOnlyList LoadBalance(IReadOnlyList hosts) { - lock (m_hostSessions) - return m_hostSessions.OrderBy(static x => x.Value).Select(static x => x.Key).ToList(); + lock (hostSessions) + return hostSessions.OrderBy(static x => x.Value).Select(static x => x.Key).ToList(); } - - private readonly Dictionary m_hostSessions; } - private sealed class ConnectionStringPool + private sealed class ConnectionStringPool(string connectionString, ConnectionPool? pool) { - public ConnectionStringPool(string connectionString, ConnectionPool? pool) - { - ConnectionString = connectionString; - Pool = pool; - } - - public string ConnectionString { get; } - public ConnectionPool? Pool { get; } + public string ConnectionString { get; } = connectionString; + public ConnectionPool? Pool { get; } = pool; } static ConnectionPool() diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs index 99c66d702..b4e9c87be 100644 --- a/src/MySqlConnector/Core/ConnectionSettings.cs +++ b/src/MySqlConnector/Core/ConnectionSettings.cs @@ -11,7 +11,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb) ConnectionStringBuilder = csb; ConnectionString = csb.ConnectionString; - if (csb.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket || (!Utility.IsWindows() && (csb.Server.StartsWith("/", StringComparison.Ordinal) || csb.Server.StartsWith("./", StringComparison.Ordinal)))) + if (csb.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket || (!Utility.IsWindows() && (csb.Server.StartsWith('/') || csb.Server.StartsWith("./", StringComparison.Ordinal)))) { if (csb.LoadBalance != MySqlLoadBalance.RoundRobin) throw new NotSupportedException("LoadBalance not supported when ConnectionProtocol=UnixSocket"); @@ -26,7 +26,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb) if (csb.LoadBalance != MySqlLoadBalance.RoundRobin) throw new NotSupportedException("LoadBalance not supported when ConnectionProtocol=NamedPipe"); ConnectionProtocol = MySqlConnectionProtocol.NamedPipe; - HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : new[] { csb.Server }; + HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : [ csb.Server ]; PipeName = csb.PipeName; } else if (csb.ConnectionProtocol == MySqlConnectionProtocol.SharedMemory) @@ -272,7 +272,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri ConnectionString = other.ConnectionString; ConnectionProtocol = MySqlConnectionProtocol.Sockets; - HostNames = new[] { host }; + HostNames = [ host ]; LoadBalance = other.LoadBalance; Port = port; PipeName = other.PipeName; diff --git a/src/MySqlConnector/Core/DbTypeMapping.cs b/src/MySqlConnector/Core/DbTypeMapping.cs index 6fe3b096d..f72611ee7 100644 --- a/src/MySqlConnector/Core/DbTypeMapping.cs +++ b/src/MySqlConnector/Core/DbTypeMapping.cs @@ -2,24 +2,15 @@ namespace MySqlConnector.Core; -internal sealed class DbTypeMapping +internal sealed class DbTypeMapping(Type clrType, DbType[] dbTypes, Func? convert = null) { - public DbTypeMapping(Type clrType, DbType[] dbTypes, Func? convert = null) - { - ClrType = clrType; - DbTypes = dbTypes; - m_convert = convert; - } - - public Type ClrType { get; } - public DbType[] DbTypes { get; } + public Type ClrType { get; } = clrType; + public DbType[] DbTypes { get; } = dbTypes; public object DoConversion(object obj) { if (obj.GetType() == ClrType) return obj; - return m_convert is null ? Convert.ChangeType(obj, ClrType, CultureInfo.InvariantCulture)! : m_convert(obj); + return convert is null ? Convert.ChangeType(obj, ClrType, CultureInfo.InvariantCulture)! : convert(obj); } - - private readonly Func? m_convert; } diff --git a/src/MySqlConnector/Core/IValuesEnumerator.cs b/src/MySqlConnector/Core/IValuesEnumerator.cs index f53a481df..0d3cac1d3 100644 --- a/src/MySqlConnector/Core/IValuesEnumerator.cs +++ b/src/MySqlConnector/Core/IValuesEnumerator.cs @@ -12,49 +12,35 @@ internal interface IValuesEnumerator void GetValues(object[] values); } -internal sealed class DbDataReaderValuesEnumerator : IValuesEnumerator +internal sealed class DbDataReaderValuesEnumerator(DbDataReader dataReader) : IValuesEnumerator { - public DbDataReaderValuesEnumerator(DbDataReader dataReader) => m_dataReader = dataReader; + public int FieldCount => dataReader.FieldCount; - public int FieldCount => m_dataReader.FieldCount; + public ValueTask MoveNextAsync() => new(dataReader.ReadAsync()); - public ValueTask MoveNextAsync() => new ValueTask(m_dataReader.ReadAsync()); + public bool MoveNext() => dataReader.Read(); - public bool MoveNext() => m_dataReader.Read(); - - public void GetValues(object[] values) => m_dataReader.GetValues(values); - - private readonly DbDataReader m_dataReader; + public void GetValues(object[] values) => dataReader.GetValues(values); } -internal sealed class DataReaderValuesEnumerator : IValuesEnumerator +internal sealed class DataReaderValuesEnumerator(IDataReader dataReader) : IValuesEnumerator { public static IValuesEnumerator Create(IDataReader dataReader) => dataReader is DbDataReader dbDataReader ? (IValuesEnumerator) new DbDataReaderValuesEnumerator(dbDataReader) : new DataReaderValuesEnumerator(dataReader); - public DataReaderValuesEnumerator(IDataReader dataReader) => m_dataReader = dataReader; - - public int FieldCount => m_dataReader.FieldCount; + public int FieldCount => dataReader.FieldCount; public ValueTask MoveNextAsync() => new(MoveNext()); - public bool MoveNext() => m_dataReader.Read(); - - public void GetValues(object[] values) => m_dataReader.GetValues(values); + public bool MoveNext() => dataReader.Read(); - private readonly IDataReader m_dataReader; + public void GetValues(object[] values) => dataReader.GetValues(values); } -internal sealed class DataRowsValuesEnumerator : IValuesEnumerator +internal sealed class DataRowsValuesEnumerator(IEnumerable dataRows, int columnCount) : IValuesEnumerator { public static IValuesEnumerator Create(DataTable dataTable) => new DataRowsValuesEnumerator(dataTable.Rows.Cast().Where(static x => x is not null).Select(static x => x!), dataTable.Columns.Count); - public DataRowsValuesEnumerator(IEnumerable dataRows, int columnCount) - { - m_dataRows = dataRows.GetEnumerator(); - FieldCount = columnCount; - } - - public int FieldCount { get; } + public int FieldCount { get; } = columnCount; public ValueTask MoveNextAsync() => new(MoveNext()); @@ -73,5 +59,5 @@ public void GetValues(object[] values) values[i] = row[i]; } - private readonly IEnumerator m_dataRows; + private readonly IEnumerator m_dataRows = dataRows.GetEnumerator(); } diff --git a/src/MySqlConnector/Core/ParsedStatement.cs b/src/MySqlConnector/Core/ParsedStatement.cs index d04ca7236..489a81a00 100644 --- a/src/MySqlConnector/Core/ParsedStatement.cs +++ b/src/MySqlConnector/Core/ParsedStatement.cs @@ -15,17 +15,17 @@ internal sealed class ParsedStatement /// The names of the parameters (if known) of the parameters in the prepared statement. There /// is one entry in this list for each parameter, which will be null if the name is unknown. /// - public List ParameterNames { get; } = new(); + public List ParameterNames { get; } = []; /// /// The normalized names of the parameters (if known) of the parameters in the prepared statement. There /// is one entry in this list for each parameter, which will be null if the name is unknown. /// - public List NormalizedParameterNames { get; } = new(); + public List NormalizedParameterNames { get; } = []; /// /// The indexes of the parameters in the prepared statement. There is one entry in this list for /// each parameter; it will be -1 if the parameter is named. /// - public List ParameterIndexes { get; } = new(); + public List ParameterIndexes { get; } = []; } diff --git a/src/MySqlConnector/Core/ParsedStatements.cs b/src/MySqlConnector/Core/ParsedStatements.cs index 9c224ebc8..fa429edf0 100644 --- a/src/MySqlConnector/Core/ParsedStatements.cs +++ b/src/MySqlConnector/Core/ParsedStatements.cs @@ -6,23 +6,14 @@ namespace MySqlConnector.Core; /// wraps a collection of objects. /// It implements to return the memory backing the statements to a shared pool. /// -internal sealed class ParsedStatements : IDisposable +internal sealed class ParsedStatements(List statements, PayloadData payloadData) : IDisposable { - public IReadOnlyList Statements => m_statements; + public IReadOnlyList Statements => statements; public void Dispose() { - m_statements.Clear(); - m_payloadData.Dispose(); - m_payloadData = default; + statements.Clear(); + payloadData.Dispose(); + payloadData = default; } - - internal ParsedStatements(List statements, PayloadData payloadData) - { - m_statements = statements; - m_payloadData = payloadData; - } - - private readonly List m_statements; - private PayloadData m_payloadData; } diff --git a/src/MySqlConnector/Core/PreparedStatement.cs b/src/MySqlConnector/Core/PreparedStatement.cs index ea9622d55..cbba24ea6 100644 --- a/src/MySqlConnector/Core/PreparedStatement.cs +++ b/src/MySqlConnector/Core/PreparedStatement.cs @@ -5,18 +5,10 @@ namespace MySqlConnector.Core; /// /// is a statement that has been prepared on the MySQL Server. /// -internal sealed class PreparedStatement +internal sealed class PreparedStatement(int statementId, ParsedStatement statement, ColumnDefinitionPayload[]? columns, ColumnDefinitionPayload[]? parameters) { - public PreparedStatement(int statementId, ParsedStatement statement, ColumnDefinitionPayload[]? columns, ColumnDefinitionPayload[]? parameters) - { - StatementId = statementId; - Statement = statement; - Columns = columns; - Parameters = parameters; - } - - public int StatementId { get; } - public ParsedStatement Statement { get; } - public ColumnDefinitionPayload[]? Columns { get; set; } - public ColumnDefinitionPayload[]? Parameters { get; } + public int StatementId { get; } = statementId; + public ParsedStatement Statement { get; } = statement; + public ColumnDefinitionPayload[]? Columns { get; set; } = columns; + public ColumnDefinitionPayload[]? Parameters { get; } = parameters; } diff --git a/src/MySqlConnector/Core/PreparedStatements.cs b/src/MySqlConnector/Core/PreparedStatements.cs index f4798a141..1bbd32e5a 100644 --- a/src/MySqlConnector/Core/PreparedStatements.cs +++ b/src/MySqlConnector/Core/PreparedStatements.cs @@ -1,20 +1,12 @@ namespace MySqlConnector.Core; -internal sealed class PreparedStatements : IDisposable +internal sealed class PreparedStatements(IReadOnlyList preparedStatements, ParsedStatements parsedStatements) : IDisposable { - public IReadOnlyList Statements { get; } - - public PreparedStatements(IReadOnlyList preparedStatements, ParsedStatements parsedStatements) - { - Statements = preparedStatements; - m_parsedStatements = parsedStatements; - } + public IReadOnlyList Statements { get; } = preparedStatements; public void Dispose() { - m_parsedStatements?.Dispose(); - m_parsedStatements = null; + parsedStatements?.Dispose(); + parsedStatements = null!; } - - private ParsedStatements? m_parsedStatements; } diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs index 9511722b3..c3704b826 100644 --- a/src/MySqlConnector/Core/ResultSet.cs +++ b/src/MySqlConnector/Core/ResultSet.cs @@ -9,13 +9,8 @@ namespace MySqlConnector.Core; -internal sealed class ResultSet +internal sealed class ResultSet(MySqlDataReader dataReader) { - public ResultSet(MySqlDataReader dataReader) - { - DataReader = dataReader; - } - public void Reset() { // ResultSet can be re-used, so initialize everything @@ -339,8 +334,12 @@ public bool HasRows public int GetOrdinal(string name) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(name); +#else if (name is null) throw new ArgumentNullException(nameof(name)); +#endif if (!HasResultSet) throw new InvalidOperationException("There is no current result set."); @@ -360,7 +359,7 @@ public Row GetCurrentRow() return m_row ?? throw new InvalidOperationException("There is no current row."); } - public MySqlDataReader DataReader { get; } + public MySqlDataReader DataReader { get; } = dataReader; public ExceptionDispatchInfo? ReadResultSetHeaderException { get; private set; } public IMySqlCommand Command => DataReader.Command!; public MySqlConnection Connection => DataReader.Connection!; diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs index e5b2c06f0..7025a8b30 100644 --- a/src/MySqlConnector/Core/Row.cs +++ b/src/MySqlConnector/Core/Row.cs @@ -76,8 +76,13 @@ public void SetData(ReadOnlyMemory data) public object GetValue(int ordinal) { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(ordinal); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(ordinal, ResultSet.ColumnDefinitions!.Length); +#else if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length) throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}"); +#endif if (m_dataOffsetLengths[ordinal].Offset == -1) return DBNull.Value; @@ -418,7 +423,13 @@ public MySqlDecimal GetMySqlDecimal(int ordinal) public int GetValues(object[] values) { - int count = Math.Min((values ?? throw new ArgumentNullException(nameof(values))).Length, ResultSet.ColumnDefinitions!.Length); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(values); +#else + if (values is null) + throw new ArgumentNullException(nameof(values)); +#endif + int count = Math.Min(values.Length, ResultSet.ColumnDefinitions!.Length); for (int i = 0; i < count; i++) values[i] = GetValue(i); return count; @@ -451,6 +462,13 @@ private void CheckBinaryColumn(int ordinal) private static void CheckBufferArguments(long dataOffset, T[] buffer, int bufferOffset, int length) { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(dataOffset); + ArgumentOutOfRangeException.ThrowIfGreaterThan(dataOffset, int.MaxValue); + ArgumentOutOfRangeException.ThrowIfNegative(length); + ArgumentOutOfRangeException.ThrowIfNegative(bufferOffset); + ArgumentOutOfRangeException.ThrowIfGreaterThan(bufferOffset, buffer.Length); +#else if (dataOffset < 0) throw new ArgumentOutOfRangeException(nameof(dataOffset), dataOffset, nameof(dataOffset) + " must be non-negative"); if (dataOffset > int.MaxValue) @@ -461,18 +479,13 @@ private static void CheckBufferArguments(long dataOffset, T[] buffer, int buf throw new ArgumentOutOfRangeException(nameof(bufferOffset), bufferOffset, nameof(bufferOffset) + " must be non-negative"); if (bufferOffset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(bufferOffset), bufferOffset, nameof(bufferOffset) + " must be within the buffer"); +#endif if (checked(bufferOffset + length) > buffer.Length) throw new ArgumentException(nameof(bufferOffset) + " + " + nameof(length) + " cannot exceed " + nameof(buffer) + "." + nameof(buffer.Length), nameof(length)); } - private readonly struct OffsetLength + private readonly struct OffsetLength(int offset, int length) { - public OffsetLength(int offset, int length) - { - Offset = offset; - Length = length; - } - public static implicit operator OffsetLength((int Offset, int Length) x) => new(x.Offset, x.Length); public void Deconstruct(out int offset, out int length) @@ -481,8 +494,8 @@ public void Deconstruct(out int offset, out int length) length = Length; } - public int Offset { get; } - public int Length { get; } + public int Offset { get; } = offset; + public int Length { get; } = length; } private readonly bool m_isBinary; diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs index 3c87b0de1..1ca8b015e 100644 --- a/src/MySqlConnector/Core/SchemaProvider.cs +++ b/src/MySqlConnector/Core/SchemaProvider.cs @@ -3,20 +3,15 @@ namespace MySqlConnector.Core; -internal sealed partial class SchemaProvider +internal sealed partial class SchemaProvider(MySqlConnection connection) { - public SchemaProvider(MySqlConnection connection) - { - m_connection = connection; - } - private void DoFillDataSourceInformation(DataTable dataTable) { var row = dataTable.NewRow(); row["CompositeIdentifierSeparatorPattern"] = @"\."; row["DataSourceProductName"] = "MySQL"; - row["DataSourceProductVersion"] = m_connection.ServerVersion; - row["DataSourceProductVersionNormalized"] = GetVersion(m_connection.Session.ServerVersion.Version); + row["DataSourceProductVersion"] = connection.ServerVersion; + row["DataSourceProductVersionNormalized"] = GetVersion(connection.Session.ServerVersion.Version); row["GroupByBehavior"] = GroupByBehavior.Unrelated; row["IdentifierPattern"] = @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)"; row["IdentifierCase"] = IdentifierCase.Insensitive; @@ -116,8 +111,8 @@ private static void DoFillReservedWords(DataTable dataTable) // (but has a higher version number) // select word from information_schema.keywords where reserved = 1; on MySQL Server 8.0.18 - var reservedWords = new[] - { + string[] reservedWords = + [ "ACCESSIBLE", "ADD", "ALL", @@ -380,7 +375,7 @@ private static void DoFillReservedWords(DataTable dataTable) "XOR", "YEAR_MONTH", "ZEROFILL", - }; + ]; foreach (string word in reservedWords) dataTable.Rows.Add(word); @@ -389,29 +384,29 @@ private static void DoFillReservedWords(DataTable dataTable) private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, List>? columns, CancellationToken cancellationToken) { Action? close = null; - if (m_connection.State != ConnectionState.Open) + if (connection.State != ConnectionState.Open) { - await m_connection.OpenAsync(ioBehavior, cancellationToken).ConfigureAwait(false); - close = m_connection.Close; + await connection.OpenAsync(ioBehavior, cancellationToken).ConfigureAwait(false); + close = connection.Close; } // remove columns that the server doesn't support if (dataTable.TableName == "Columns") { - using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'GENERATION_EXPRESSION';", m_connection)) + using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'GENERATION_EXPRESSION';", connection)) { if (await command.ExecuteScalarAsync(ioBehavior, cancellationToken).ConfigureAwait(false) is null) dataTable.Columns.Remove("GENERATION_EXPRESSION"); } - using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'SRS_ID';", m_connection)) + using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'SRS_ID';", connection)) { if (await command.ExecuteScalarAsync(ioBehavior, cancellationToken).ConfigureAwait(false) is null) dataTable.Columns.Remove("SRS_ID"); } } - using (var command = m_connection.CreateCommand()) + using (var command = connection.CreateCommand()) { #pragma warning disable CA2100 command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName; @@ -434,6 +429,4 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable close?.Invoke(); } - - private readonly MySqlConnection m_connection; } diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs index 7a9c5843a..11c75e185 100644 --- a/src/MySqlConnector/Core/SchemaProvider.g.cs +++ b/src/MySqlConnector/Core/SchemaProvider.g.cs @@ -9,8 +9,12 @@ internal sealed partial class SchemaProvider { public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string collectionName, string?[]? restrictionValues, CancellationToken cancellationToken) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(collectionName); +#else if (collectionName is null) throw new ArgumentNullException(nameof(collectionName)); +#endif var dataTable = new DataTable(); if (string.Equals(collectionName, "MetaDataCollections", StringComparison.OrdinalIgnoreCase)) @@ -83,12 +87,12 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT throw new ArgumentException("restrictionValues is not supported for schema 'MetaDataCollections'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CollectionName", typeof(string)), new("NumberOfRestrictions", typeof(int)), new("NumberOfIdentifierParts", typeof(int)), - }); + ]); dataTable.Rows.Add("MetaDataCollections", 0, 0); dataTable.Rows.Add("CharacterSets", 0, 0); @@ -129,13 +133,13 @@ private async Task FillCharacterSetsAsync(IOBehavior ioBehavior, DataTable dataT throw new ArgumentException("restrictionValues is not supported for schema 'CharacterSets'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CHARACTER_SET_NAME", typeof(string)), new("DEFAULT_COLLATE_NAME", typeof(string)), new("DESCRIPTION", typeof(string)), new("MAXLEN", typeof(int)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "CHARACTER_SETS", null, cancellationToken).ConfigureAwait(false); } @@ -146,15 +150,15 @@ private async Task FillCollationsAsync(IOBehavior ioBehavior, DataTable dataTabl throw new ArgumentException("restrictionValues is not supported for schema 'Collations'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("COLLATION_NAME", typeof(string)), new("CHARACTER_SET_NAME", typeof(string)), new("ID", typeof(int)), new("IS_DEFAULT", typeof(string)), new("IS_COMPILED", typeof(string)), new("SORTLEN", typeof(int)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "COLLATIONS", null, cancellationToken).ConfigureAwait(false); } @@ -165,11 +169,11 @@ private async Task FillCollationCharacterSetApplicabilityAsync(IOBehavior ioBeha throw new ArgumentException("restrictionValues is not supported for schema 'CollationCharacterSetApplicability'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("COLLATION_NAME", typeof(string)), new("CHARACTER_SET_NAME", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "COLLATION_CHARACTER_SET_APPLICABILITY", null, cancellationToken).ConfigureAwait(false); } @@ -180,8 +184,8 @@ private async Task FillColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Columns'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), @@ -203,7 +207,7 @@ private async Task FillColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, new("COLUMN_COMMENT", typeof(string)), new("GENERATION_EXPRESSION", typeof(string)), new("SRS_ID", typeof(string)), - }); + ]); var columns = new List>(); if (restrictionValues is not null) @@ -227,14 +231,14 @@ private async Task FillDatabasesAsync(IOBehavior ioBehavior, DataTable dataTable throw new ArgumentException("restrictionValues is not supported for schema 'Databases'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CATALOG_NAME", typeof(string)), new("SCHEMA_NAME", typeof(string)), new("DEFAULT_CHARACTER_SET_NAME", typeof(string)), new("DEFAULT_COLLATION_NAME", typeof(string)), new("SQL_PATH", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "SCHEMATA", null, cancellationToken).ConfigureAwait(false); } @@ -245,8 +249,8 @@ private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dat throw new ArgumentException("restrictionValues is not supported for schema 'DataSourceInformation'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CompositeIdentifierSeparatorPattern", typeof(string)), new("DataSourceProductName", typeof(string)), new("DataSourceProductVersion", typeof(string)), @@ -264,7 +268,7 @@ private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dat new("StatementSeparatorPattern", typeof(string)), new("StringLiteralPattern", typeof(string)), new("SupportedJoinOperators", typeof(SupportedJoinOperators)), - }); + ]); DoFillDataSourceInformation(dataTable); @@ -277,8 +281,8 @@ private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, stri throw new ArgumentException("restrictionValues is not supported for schema 'DataTypes'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TypeName", typeof(string)), new("ProviderDbType", typeof(int)), new("ColumnSize", typeof(long)), @@ -302,7 +306,7 @@ private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, stri new("LiteralPrefix", typeof(string)), new("LiteralSuffix", typeof(string)), new("NativeDataType", typeof(string)), - }); + ]); DoFillDataTypes(dataTable); @@ -315,15 +319,15 @@ private async Task FillEnginesAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("restrictionValues is not supported for schema 'Engines'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("ENGINE", typeof(string)), new("SUPPORT", typeof(string)), new("COMMENT", typeof(string)), new("TRANSACTIONS", typeof(string)), new("XA", typeof(string)), new("SAVEPOINTS", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "ENGINES", null, cancellationToken).ConfigureAwait(false); } @@ -334,8 +338,8 @@ private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable data throw new ArgumentException("restrictionValues is not supported for schema 'KeyColumnUsage'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CONSTRAINT_CATALOG", typeof(string)), new("CONSTRAINT_SCHEMA", typeof(string)), new("CONSTRAINT_NAME", typeof(string)), @@ -348,7 +352,7 @@ private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable data new("REFERENCED_TABLE_SCHEMA", typeof(string)), new("REFERENCED_TABLE_NAME", typeof(string)), new("REFERENCED_COLUMN_NAME", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "KEY_COLUMN_USAGE", null, cancellationToken).ConfigureAwait(false); } @@ -359,11 +363,11 @@ private async Task FillKeyWordsAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("restrictionValues is not supported for schema 'KeyWords'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("WORD", typeof(string)), new("RESERVED", typeof(int)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "KEYWORDS", null, cancellationToken).ConfigureAwait(false); } @@ -374,8 +378,8 @@ private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTabl throw new ArgumentException("restrictionValues is not supported for schema 'Parameters'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("SPECIFIC_CATALOG", typeof(string)), new("SPECIFIC_SCHEMA", typeof(string)), new("SPECIFIC_NAME", typeof(string)), @@ -392,7 +396,7 @@ private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTabl new("COLLATION_NAME", typeof(string)), new("DTD_IDENTIFIER", typeof(string)), new("ROUTINE_TYPE", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "PARAMETERS", null, cancellationToken).ConfigureAwait(false); } @@ -403,8 +407,8 @@ private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTabl throw new ArgumentException("restrictionValues is not supported for schema 'Partitions'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), @@ -430,7 +434,7 @@ private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTabl new("PARTITION_COMMENT", typeof(string)), new("NODEGROUP", typeof(string)), new("TABLESPACE_NAME", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "PARTITIONS", null, cancellationToken).ConfigureAwait(false); } @@ -441,8 +445,8 @@ private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("restrictionValues is not supported for schema 'Plugins'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("PLUGIN_NAME", typeof(string)), new("PLUGIN_VERSION", typeof(string)), new("PLUGIN_STATUS", typeof(string)), @@ -454,7 +458,7 @@ private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable, new("PLUGIN_DESCRIPTION", typeof(string)), new("PLUGIN_LICENSE", typeof(string)), new("LOAD_OPTION", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "PLUGINS", null, cancellationToken).ConfigureAwait(false); } @@ -465,8 +469,8 @@ private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTabl throw new ArgumentException("restrictionValues is not supported for schema 'Procedures'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("SPECIFIC_NAME", typeof(string)), new("ROUTINE_CATALOG", typeof(string)), new("ROUTINE_SCHEMA", typeof(string)), @@ -487,7 +491,7 @@ private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTabl new("SQL_MODE", typeof(string)), new("ROUTINE_COMMENT", typeof(string)), new("DEFINER", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "ROUTINES", null, cancellationToken).ConfigureAwait(false); } @@ -498,8 +502,8 @@ private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTab throw new ArgumentException("restrictionValues is not supported for schema 'ProcessList'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("ID", typeof(long)), new("USER", typeof(string)), new("HOST", typeof(string)), @@ -508,7 +512,7 @@ private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTab new("TIME", typeof(int)), new("STATE", typeof(string)), new("INFO", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "PROCESSLIST", null, cancellationToken).ConfigureAwait(false); } @@ -519,8 +523,8 @@ private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable throw new ArgumentException("restrictionValues is not supported for schema 'Profiling'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("QUERY_ID", typeof(int)), new("SEQ", typeof(int)), new("STATE", typeof(string)), @@ -539,7 +543,7 @@ private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable new("SOURCE_FUNCTION", typeof(string)), new("SOURCE_FILE", typeof(string)), new("SOURCE_LINE", typeof(int)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "PROFILING", null, cancellationToken).ConfigureAwait(false); } @@ -550,8 +554,8 @@ private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTa throw new ArgumentException("restrictionValues is not supported for schema 'ReferentialConstraints'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CONSTRAINT_CATALOG", typeof(string)), new("CONSTRAINT_SCHEMA", typeof(string)), new("CONSTRAINT_NAME", typeof(string)), @@ -563,7 +567,7 @@ private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTa new("DELETE_RULE", typeof(string)), new("TABLE_NAME", typeof(string)), new("REFERENCED_TABLE_NAME", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "REFERENTIAL_CONSTRAINTS", null, cancellationToken).ConfigureAwait(false); } @@ -574,10 +578,10 @@ private Task FillReservedWordsAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("restrictionValues is not supported for schema 'ReservedWords'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("ReservedWord", typeof(string)), - }); + ]); DoFillReservedWords(dataTable); @@ -590,14 +594,14 @@ private async Task FillResourceGroupsAsync(IOBehavior ioBehavior, DataTable data throw new ArgumentException("restrictionValues is not supported for schema 'ResourceGroups'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("RESOURCE_GROUP_NAME", typeof(string)), new("RESOURCE_GROUP_TYPE", typeof(string)), new("RESOURCE_GROUP_ENABLED", typeof(int)), new("VCPU_IDS", typeof(string)), new("THREAD_PRIORITY", typeof(int)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "RESOURCE_GROUPS", null, cancellationToken).ConfigureAwait(false); } @@ -608,13 +612,13 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s throw new ArgumentException("restrictionValues is not supported for schema 'Restrictions'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CollectionName", typeof(string)), new("RestrictionName", typeof(string)), new("RestrictionDefault", typeof(string)), new("RestrictionNumber", typeof(int)), - }); + ]); dataTable.Rows.Add("Columns", "Catalog", "TABLE_CATALOG", 1); dataTable.Rows.Add("Columns", "Schema", "TABLE_SCHEMA", 2); @@ -634,14 +638,14 @@ private async Task FillSchemaPrivilegesAsync(IOBehavior ioBehavior, DataTable da throw new ArgumentException("restrictionValues is not supported for schema 'SchemaPrivileges'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("GRANTEE", typeof(string)), new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("PRIVILEGE_TYPE", typeof(string)), new("IS_GRANTABLE", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "SCHEMA_PRIVILEGES", null, cancellationToken).ConfigureAwait(false); } @@ -652,8 +656,8 @@ private async Task FillTablesAsync(IOBehavior ioBehavior, DataTable dataTable, s throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Tables'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), @@ -675,7 +679,7 @@ private async Task FillTablesAsync(IOBehavior ioBehavior, DataTable dataTable, s new("CHECKSUM", typeof(string)), new("CREATE_OPTIONS", typeof(string)), new("TABLE_COMMENT", typeof(string)), - }); + ]); var columns = new List>(); if (restrictionValues is not null) @@ -699,15 +703,15 @@ private async Task FillTableConstraintsAsync(IOBehavior ioBehavior, DataTable da throw new ArgumentException("restrictionValues is not supported for schema 'TableConstraints'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("CONSTRAINT_CATALOG", typeof(string)), new("CONSTRAINT_SCHEMA", typeof(string)), new("CONSTRAINT_NAME", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), new("CONSTRAINT_TYPE", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "TABLE_CONSTRAINTS", null, cancellationToken).ConfigureAwait(false); } @@ -718,15 +722,15 @@ private async Task FillTablePrivilegesAsync(IOBehavior ioBehavior, DataTable dat throw new ArgumentException("restrictionValues is not supported for schema 'TablePrivileges'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("GRANTEE", typeof(string)), new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), new("PRIVILEGE_TYPE", typeof(string)), new("IS_GRANTABLE", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "TABLE_PRIVILEGES", null, cancellationToken).ConfigureAwait(false); } @@ -737,8 +741,8 @@ private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTab throw new ArgumentException("restrictionValues is not supported for schema 'TableSpaces'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TABLESPACE_NAME", typeof(string)), new("ENGINE", typeof(string)), new("TABLESPACE_TYPE", typeof(string)), @@ -748,7 +752,7 @@ private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTab new("MAXIMUM_SIZE", typeof(long)), new("NODEGROUP_ID", typeof(long)), new("TABLESPACE_COMMENT", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "TABLESPACES", null, cancellationToken).ConfigureAwait(false); } @@ -759,8 +763,8 @@ private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable, throw new ArgumentException("restrictionValues is not supported for schema 'Triggers'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TRIGGER_CATALOG", typeof(string)), new("TRIGGER_SCHEMA", typeof(string)), new("TRIGGER_NAME", typeof(string)), @@ -783,7 +787,7 @@ private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable, new("CHARACTER_SET_CLIENT", typeof(string)), new("COLLATION_CONNECTION", typeof(string)), new("DATABASE_COLLATION", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "TRIGGERS", null, cancellationToken).ConfigureAwait(false); } @@ -794,13 +798,13 @@ private async Task FillUserPrivilegesAsync(IOBehavior ioBehavior, DataTable data throw new ArgumentException("restrictionValues is not supported for schema 'UserPrivileges'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("GRANTEE", typeof(string)), new("TABLE_CATALOG", typeof(string)), new("PRIVILEGE_TYPE", typeof(string)), new("IS_GRANTABLE", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "USER_PRIVILEGES", null, cancellationToken).ConfigureAwait(false); } @@ -811,8 +815,8 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st throw new ArgumentException("restrictionValues is not supported for schema 'Views'.", nameof(restrictionValues)); dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ new("TABLE_CATALOG", typeof(string)), new("TABLE_SCHEMA", typeof(string)), new("TABLE_NAME", typeof(string)), @@ -823,7 +827,7 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st new("SECURITY_TYPE", typeof(string)), new("CHARACTER_SET_CLIENT", typeof(string)), new("COLLATION_CONNECTION", typeof(string)), - }); + ]); await FillDataTableAsync(ioBehavior, dataTable, "VIEWS", null, cancellationToken).ConfigureAwait(false); } diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs index e8aeb45e7..351eda129 100644 --- a/src/MySqlConnector/Core/ServerSession.cs +++ b/src/MySqlConnector/Core/ServerSession.cs @@ -41,7 +41,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i Pool = pool; PoolGeneration = poolGeneration; HostName = ""; - m_activityTags = new ActivityTagsCollection(); + m_activityTags = []; DataReader = new(); Log.CreatedNewSession(m_logger, Id); } @@ -267,7 +267,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can preparedStatements.Add(new(response.StatementId, statement, columns, parameters)); } - m_preparedStatements ??= new(); + m_preparedStatements ??= []; m_preparedStatements.Add(commandText, new(preparedStatements, parsedStatements)); } @@ -850,7 +850,7 @@ private async Task GetRsaPublicKeyAsync(string switchRequestName, Connec { // request the RSA public key var payloadContent = switchRequestName == "caching_sha2_password" ? (byte) 0x02 : (byte) 0x01; - await SendReplyAsync(new PayloadData(new[] { payloadContent }), ioBehavior, cancellationToken).ConfigureAwait(false); + await SendReplyAsync(new PayloadData([ payloadContent ]), ioBehavior, cancellationToken).ConfigureAwait(false); var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false); var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload.Span); return Encoding.ASCII.GetString(publicKeyPayload.Data); @@ -1309,7 +1309,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect "CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key"); } m_clientCertificate = certificate; - clientCertificates = new() { certificate }; + clientCertificates = [certificate]; } catch (CryptographicException ex) { @@ -1322,7 +1322,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect if (clientCertificates is null && connection.ProvideClientCertificatesCallback is { } clientCertificatesProvider) { - clientCertificates = new(); + clientCertificates = []; try { await clientCertificatesProvider(clientCertificates).ConfigureAwait(false); @@ -1456,8 +1456,10 @@ caCertificateChain is not null && }; #if NETCOREAPP3_0_OR_GREATER +#pragma warning disable CA1416 // Validate platform compatibility if (cs.TlsCipherSuites is { Count: > 0 }) clientAuthenticationOptions.CipherSuitesPolicy = new CipherSuitesPolicy(cs.TlsCipherSuites); +#pragma warning restore CA1416 // Validate platform compatibility #endif try @@ -1530,7 +1532,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica m_clientCertificate = new X509Certificate2(m_clientCertificate.Export(X509ContentType.Pkcs12)); oldCertificate.Dispose(); } - return new() { m_clientCertificate }; + return [m_clientCertificate]; #else Log.LoadingClientKeyFromKeyFile(m_logger, Id, sslKeyFile); string keyPem; @@ -1588,7 +1590,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica #endif m_clientCertificate = certificate; - return new() { certificate }; + return [certificate]; } catch (CryptographicException ex) { @@ -1622,7 +1624,7 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs) return true; // detect Azure Database for MySQL DNS suffixes, if a "user@host" user ID is being used - if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.UserID.IndexOf('@') != -1) + if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.UserID.Contains('@')) { return HostName.EndsWith(".mysql.database.azure.com", StringComparison.OrdinalIgnoreCase) || HostName.EndsWith(".database.windows.net", StringComparison.OrdinalIgnoreCase) || @@ -1810,7 +1812,7 @@ private byte[] CreateConnectionAttributes(string programName) return payload.Memory.ToArray(); } - private Exception CreateExceptionForErrorPayload(ReadOnlySpan span) + private MySqlException CreateExceptionForErrorPayload(ReadOnlySpan span) { var errorPayload = ErrorPayload.Create(span); Log.ErrorPayload(m_logger, Id, errorPayload.ErrorCode, errorPayload.State, errorPayload.Message); @@ -1882,23 +1884,18 @@ private enum State Failed, } - private sealed class DelimiterSqlParser : SqlParser + private sealed class DelimiterSqlParser(IMySqlCommand command) + : SqlParser(new StatementPreparer(command.CommandText!, null, command.CreateStatementPreparerOptions())) { - public DelimiterSqlParser(IMySqlCommand command) - : base(new StatementPreparer(command.CommandText!, null, command.CreateStatementPreparerOptions())) - { - m_sql = command.CommandText!; - } - public bool HasDelimiter { get; private set; } protected override void OnStatementBegin(int index) { - if (index + 10 < m_sql.Length && m_sql.AsSpan(index, 10).Equals("delimiter ".AsSpan(), StringComparison.OrdinalIgnoreCase)) + if (index + 10 < Sql.Length && Sql.AsSpan(index, 10).Equals("delimiter ".AsSpan(), StringComparison.OrdinalIgnoreCase)) HasDelimiter = true; } - private readonly string m_sql; + private string Sql { get; } = command.CommandText!; } [LoggerMessage(EventIds.CannotExecuteNewCommandInState, LogLevel.Error, "Session {SessionId} can't execute new command when in state {SessionState}")] diff --git a/src/MySqlConnector/Core/SqlParser.cs b/src/MySqlConnector/Core/SqlParser.cs index a5c312e0e..b2240de89 100644 --- a/src/MySqlConnector/Core/SqlParser.cs +++ b/src/MySqlConnector/Core/SqlParser.cs @@ -2,15 +2,19 @@ namespace MySqlConnector.Core; -internal abstract class SqlParser +internal abstract class SqlParser(StatementPreparer preparer) { - protected StatementPreparer Preparer { get; } - - protected SqlParser(StatementPreparer preparer) => Preparer = preparer; + protected StatementPreparer Preparer { get; } = preparer; public void Parse(string sql) { - OnBeforeParse(sql ?? throw new ArgumentNullException(nameof(sql))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(sql); +#else + if (sql is null) + throw new ArgumentNullException(nameof(sql)); +#endif + OnBeforeParse(sql); int parameterStartIndex = -1; var noBackslashEscapes = (Preparer.Options & StatementPreparerOptions.NoBackslashEscapes) == StatementPreparerOptions.NoBackslashEscapes; diff --git a/src/MySqlConnector/Core/StandardEnlistedTransaction.cs b/src/MySqlConnector/Core/StandardEnlistedTransaction.cs index 45eff3b7c..32222aca5 100644 --- a/src/MySqlConnector/Core/StandardEnlistedTransaction.cs +++ b/src/MySqlConnector/Core/StandardEnlistedTransaction.cs @@ -3,13 +3,9 @@ namespace MySqlConnector.Core; -internal sealed class StandardEnlistedTransaction : EnlistedTransactionBase +internal sealed class StandardEnlistedTransaction(Transaction transaction, MySqlConnection connection) + : EnlistedTransactionBase(transaction, connection) { - public StandardEnlistedTransaction(Transaction transaction, MySqlConnection connection) - : base(transaction, connection) - { - } - protected override void OnStart() { var isolationLevel = Transaction.IsolationLevel switch diff --git a/src/MySqlConnector/Core/StatementPreparer.cs b/src/MySqlConnector/Core/StatementPreparer.cs index 12e8d5689..8e53a6203 100644 --- a/src/MySqlConnector/Core/StatementPreparer.cs +++ b/src/MySqlConnector/Core/StatementPreparer.cs @@ -4,24 +4,17 @@ namespace MySqlConnector.Core; -internal sealed class StatementPreparer +internal sealed class StatementPreparer(string commandText, MySqlParameterCollection? parameters, StatementPreparerOptions options) { - public StatementPreparerOptions Options { get; } - - public StatementPreparer(string commandText, MySqlParameterCollection? parameters, StatementPreparerOptions options) - { - m_commandText = commandText; - m_parameters = parameters; - Options = options; - } + public StatementPreparerOptions Options { get; } = options; public ParsedStatements SplitStatements() { var statements = new List(); var statementStartEndIndexes = new List(); - var writer = new ByteBufferWriter(m_commandText.Length + 1); + var writer = new ByteBufferWriter(CommandText.Length + 1); var parser = new PreparedCommandSqlParser(this, statements, statementStartEndIndexes, writer); - parser.Parse(m_commandText); + parser.Parse(CommandText); for (var i = 0; i < statements.Count; i++) #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER statements[i].StatementBytes = writer.ArraySegment[statementStartEndIndexes[i * 2]..statementStartEndIndexes[i * 2 + 1]]; @@ -33,10 +26,10 @@ public ParsedStatements SplitStatements() public bool ParseAndBindParameters(ByteBufferWriter writer) { - if (!string.IsNullOrWhiteSpace(m_commandText)) + if (!string.IsNullOrWhiteSpace(CommandText)) { var parser = new ParameterSqlParser(this, writer); - parser.Parse(m_commandText); + parser.Parse(CommandText); return parser.IsComplete; } return true; @@ -44,7 +37,7 @@ public bool ParseAndBindParameters(ByteBufferWriter writer) private int GetParameterIndex(string name) { - var index = m_parameters?.NormalizedIndexOf(name) ?? -1; + var index = parameters?.NormalizedIndexOf(name) ?? -1; if (index == -1 && (Options & StatementPreparerOptions.AllowUserVariables) == 0) throw new MySqlException($"Parameter '{name}' must be defined. To use this as a variable, set 'Allow User Variables=true' in the connection string."); return index; @@ -52,27 +45,22 @@ private int GetParameterIndex(string name) private MySqlParameter GetInputParameter(int index) { - if (index >= (m_parameters?.Count ?? 0)) - throw new MySqlException($"Parameter index {index} is invalid when only {m_parameters?.Count ?? 0} parameter{(m_parameters?.Count == 1 ? " is" : "s are")} defined."); - var parameter = m_parameters![index]; + if (index >= (parameters?.Count ?? 0)) + throw new MySqlException($"Parameter index {index} is invalid when only {parameters?.Count ?? 0} parameter{(parameters?.Count == 1 ? " is" : "s are")} defined."); + var parameter = parameters![index]; if (parameter.Direction != ParameterDirection.Input && (Options & StatementPreparerOptions.AllowOutputParameters) == 0) throw new MySqlException($"Only ParameterDirection.Input is supported when CommandType is Text (parameter name: {parameter.ParameterName})"); return parameter; } - private sealed class ParameterSqlParser : SqlParser + private sealed class ParameterSqlParser(StatementPreparer preparer, ByteBufferWriter writer) + : SqlParser(preparer) { - public ParameterSqlParser(StatementPreparer preparer, ByteBufferWriter writer) - : base(preparer) - { - m_writer = writer; - } - public bool IsComplete { get; private set; } protected override void OnNamedParameter(int index, int length) { - var parameterIndex = Preparer.GetParameterIndex(Preparer.m_commandText.Substring(index, length)); + var parameterIndex = Preparer.GetParameterIndex(Preparer.CommandText.Substring(index, length)); if (parameterIndex != -1) DoAppendParameter(parameterIndex, index, length); } @@ -85,48 +73,42 @@ protected override void OnPositionalParameter(int index) private void DoAppendParameter(int parameterIndex, int textIndex, int textLength) { - m_writer.Write(Preparer.m_commandText, m_lastIndex, textIndex - m_lastIndex); + Writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex); var parameter = Preparer.GetInputParameter(parameterIndex); - parameter.AppendSqlString(m_writer, Preparer.Options); + parameter.AppendSqlString(Writer, Preparer.Options); m_lastIndex = textIndex + textLength; } protected override void OnParsed(FinalParseStates states) { - m_writer.Write(Preparer.m_commandText, m_lastIndex, Preparer.m_commandText.Length - m_lastIndex); + Writer.Write(Preparer.CommandText, m_lastIndex, Preparer.CommandText.Length - m_lastIndex); if ((states & FinalParseStates.NeedsNewline) == FinalParseStates.NeedsNewline) - m_writer.Write((byte) '\n'); + Writer.Write((byte) '\n'); if ((states & FinalParseStates.NeedsSemicolon) == FinalParseStates.NeedsSemicolon && (Preparer.Options & StatementPreparerOptions.AppendSemicolon) == StatementPreparerOptions.AppendSemicolon) - m_writer.Write((byte) ';'); + Writer.Write((byte) ';'); IsComplete = (states & FinalParseStates.Complete) == FinalParseStates.Complete; } - private readonly ByteBufferWriter m_writer; + private ByteBufferWriter Writer { get; } = writer; + private int m_currentParameterIndex; private int m_lastIndex; } - private sealed class PreparedCommandSqlParser : SqlParser + private sealed class PreparedCommandSqlParser(StatementPreparer preparer, List statements, List statementStartEndIndexes, ByteBufferWriter writer) + : SqlParser(preparer) { - public PreparedCommandSqlParser(StatementPreparer preparer, List statements, List statementStartEndIndexes, ByteBufferWriter writer) - : base(preparer) - { - m_statements = statements; - m_statementStartEndIndexes = statementStartEndIndexes; - m_writer = writer; - } - protected override void OnStatementBegin(int index) { - m_statements.Add(new ParsedStatement()); - m_statementStartEndIndexes.Add(m_writer.Position); - m_writer.Write((byte) CommandKind.StatementPrepare); + Statements.Add(new ParsedStatement()); + StatementStartEndIndexes.Add(Writer.Position); + Writer.Write((byte) CommandKind.StatementPrepare); m_lastIndex = index; } protected override void OnNamedParameter(int index, int length) { - var parameterName = Preparer.m_commandText.Substring(index, length); + var parameterName = Preparer.CommandText.Substring(index, length); DoAppendParameter(parameterName, -1, index, length); } @@ -139,32 +121,32 @@ protected override void OnPositionalParameter(int index) private void DoAppendParameter(string? parameterName, int parameterIndex, int textIndex, int textLength) { // write all SQL up to the parameter - m_writer.Write(Preparer.m_commandText, m_lastIndex, textIndex - m_lastIndex); + Writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex); m_lastIndex = textIndex + textLength; // replace the parameter with a ? placeholder - m_writer.Write((byte) '?'); + Writer.Write((byte) '?'); // store the parameter index - m_statements[m_statements.Count - 1].ParameterNames.Add(parameterName); - m_statements[m_statements.Count - 1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName)); - m_statements[m_statements.Count - 1].ParameterIndexes.Add(parameterIndex); + Statements[Statements.Count - 1].ParameterNames.Add(parameterName); + Statements[Statements.Count - 1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName)); + Statements[Statements.Count - 1].ParameterIndexes.Add(parameterIndex); } protected override void OnStatementEnd(int index) { - m_writer.Write(Preparer.m_commandText, m_lastIndex, index - m_lastIndex); + Writer.Write(Preparer.CommandText, m_lastIndex, index - m_lastIndex); m_lastIndex = index; - m_statementStartEndIndexes.Add(m_writer.Position); + StatementStartEndIndexes.Add(Writer.Position); } - private readonly List m_statements; - private readonly List m_statementStartEndIndexes; - private readonly ByteBufferWriter m_writer; + private List Statements { get; } = statements; + private List StatementStartEndIndexes { get; } = statementStartEndIndexes; + private ByteBufferWriter Writer { get; } = writer; + private int m_currentParameterIndex; private int m_lastIndex; } - private readonly string m_commandText; - private readonly MySqlParameterCollection? m_parameters; + private string CommandText { get; } = commandText; } diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs index 2888ce05e..6d46c4822 100644 --- a/src/MySqlConnector/Core/TypeMapper.cs +++ b/src/MySqlConnector/Core/TypeMapper.cs @@ -13,25 +13,25 @@ internal sealed class TypeMapper private TypeMapper() { - m_columnTypeMetadata = new(); - m_dbTypeMappingsByClrType = new(); - m_dbTypeMappingsByDbType = new(); + m_columnTypeMetadata = []; + m_dbTypeMappingsByClrType = []; + m_dbTypeMappingsByDbType = []; m_columnTypeMetadataLookup = new(StringComparer.OrdinalIgnoreCase); - m_mySqlDbTypeToColumnTypeMetadata = new(); + m_mySqlDbTypeToColumnTypeMetadata = []; // boolean - var typeBoolean = AddDbTypeMapping(new(typeof(bool), new[] { DbType.Boolean }, convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture))); + var typeBoolean = AddDbTypeMapping(new(typeof(bool), [ DbType.Boolean ], convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture))); AddColumnTypeMetadata(new("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: false, length: 1, columnSize: 1, simpleDataTypeName: "BOOL", createFormat: "BOOL")); // integers - var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), new[] { DbType.SByte }, convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture))); - var typeByte = AddDbTypeMapping(new(typeof(byte), new[] { DbType.Byte }, convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture))); - var typeShort = AddDbTypeMapping(new(typeof(short), new[] { DbType.Int16 }, convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture))); - var typeUshort = AddDbTypeMapping(new(typeof(ushort), new[] { DbType.UInt16 }, convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture))); - var typeInt = AddDbTypeMapping(new(typeof(int), new[] { DbType.Int32 }, convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture))); - var typeUint = AddDbTypeMapping(new(typeof(uint), new[] { DbType.UInt32 }, convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture))); - var typeLong = AddDbTypeMapping(new(typeof(long), new[] { DbType.Int64 }, convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture))); - var typeUlong = AddDbTypeMapping(new(typeof(ulong), new[] { DbType.UInt64 }, convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture))); + var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), [ DbType.SByte ], convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture))); + var typeByte = AddDbTypeMapping(new(typeof(byte), [ DbType.Byte ], convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture))); + var typeShort = AddDbTypeMapping(new(typeof(short), [ DbType.Int16 ], convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture))); + var typeUshort = AddDbTypeMapping(new(typeof(ushort), [ DbType.UInt16 ], convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture))); + var typeInt = AddDbTypeMapping(new(typeof(int), [ DbType.Int32 ], convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture))); + var typeUint = AddDbTypeMapping(new(typeof(uint), [ DbType.UInt32 ], convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture))); + var typeLong = AddDbTypeMapping(new(typeof(long), [ DbType.Int64 ], convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture))); + var typeUlong = AddDbTypeMapping(new(typeof(ulong), [ DbType.UInt64 ], convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture))); AddColumnTypeMetadata(new("TINYINT", typeSbyte, MySqlDbType.Byte, isUnsigned: false)); AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true, length: 1)); AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true)); @@ -46,9 +46,9 @@ private TypeMapper() AddColumnTypeMetadata(new("BIT", typeUlong, MySqlDbType.Bit)); // decimals - var typeDecimal = AddDbTypeMapping(new(typeof(decimal), new[] { DbType.Decimal, DbType.Currency, DbType.VarNumeric }, convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture))); - var typeDouble = AddDbTypeMapping(new(typeof(double), new[] { DbType.Double }, convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture))); - var typeFloat = AddDbTypeMapping(new(typeof(float), new[] { DbType.Single }, convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture))); + var typeDecimal = AddDbTypeMapping(new(typeof(decimal), [ DbType.Decimal, DbType.Currency, DbType.VarNumeric ], convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture))); + var typeDouble = AddDbTypeMapping(new(typeof(double), [ DbType.Double ], convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture))); + var typeFloat = AddDbTypeMapping(new(typeof(float), [ DbType.Single ], convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture))); AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, createFormat: "DECIMAL({0},{1});precision,scale")); AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, isUnsigned: true, createFormat: "DECIMAL({0},{1}) UNSIGNED;precision,scale")); AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.Decimal)); @@ -56,8 +56,8 @@ private TypeMapper() AddColumnTypeMetadata(new("FLOAT", typeFloat, MySqlDbType.Float)); // string - var typeFixedString = AddDbTypeMapping(new(typeof(string), new[] { DbType.StringFixedLength, DbType.AnsiStringFixedLength }, convert: Convert.ToString!)); - var typeString = AddDbTypeMapping(new(typeof(string), new[] { DbType.String, DbType.AnsiString, DbType.Xml }, convert: Convert.ToString!)); + var typeFixedString = AddDbTypeMapping(new(typeof(string), [ DbType.StringFixedLength, DbType.AnsiStringFixedLength ], convert: Convert.ToString!)); + var typeString = AddDbTypeMapping(new(typeof(string), [ DbType.String, DbType.AnsiString, DbType.Xml ], convert: Convert.ToString!)); AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarChar, createFormat: "VARCHAR({0});size")); AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarString)); AddColumnTypeMetadata(new("CHAR", typeFixedString, MySqlDbType.String, createFormat: "CHAR({0});size")); @@ -70,7 +70,7 @@ private TypeMapper() AddColumnTypeMetadata(new("JSON", typeString, MySqlDbType.JSON)); // binary - var typeBinary = AddDbTypeMapping(new(typeof(byte[]), new[] { DbType.Binary })); + var typeBinary = AddDbTypeMapping(new(typeof(byte[]), [ DbType.Binary ])); AddColumnTypeMetadata(new("BLOB", typeBinary, MySqlDbType.Blob, binary: true, columnSize: ushort.MaxValue, simpleDataTypeName: "BLOB")); AddColumnTypeMetadata(new("BINARY", typeBinary, MySqlDbType.Binary, binary: true, simpleDataTypeName: "BLOB", createFormat: "BINARY({0});length")); AddColumnTypeMetadata(new("VARBINARY", typeBinary, MySqlDbType.VarBinary, binary: true, simpleDataTypeName: "BLOB", createFormat: "VARBINARY({0});length")); @@ -91,15 +91,15 @@ private TypeMapper() // date/time #if NET6_0_OR_GREATER - AddDbTypeMapping(new(typeof(DateOnly), new[] { DbType.Date })); + AddDbTypeMapping(new(typeof(DateOnly), [ DbType.Date ])); #endif - var typeDate = AddDbTypeMapping(new(typeof(DateTime), new[] { DbType.Date })); - var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), new[] { DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset })); - AddDbTypeMapping(new(typeof(DateTimeOffset), new[] { DbType.DateTimeOffset })); + var typeDate = AddDbTypeMapping(new(typeof(DateTime), [ DbType.Date ])); + var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), [ DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset ])); + AddDbTypeMapping(new(typeof(DateTimeOffset), [ DbType.DateTimeOffset ])); #if NET6_0_OR_GREATER - AddDbTypeMapping(new(typeof(TimeOnly), new[] { DbType.Time })); + AddDbTypeMapping(new(typeof(TimeOnly), [ DbType.Time ])); #endif - var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), new[] { DbType.Time }, convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture))); + var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), [ DbType.Time ], convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture))); AddColumnTypeMetadata(new("DATETIME", typeDateTime, MySqlDbType.DateTime)); AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Date)); AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Newdate)); @@ -113,11 +113,11 @@ private TypeMapper() #else Func convertGuid = static o => Guid.Parse(Convert.ToString(o, CultureInfo.InvariantCulture)!); #endif - var typeGuid = AddDbTypeMapping(new(typeof(Guid), new[] { DbType.Guid }, convert: convertGuid)); + var typeGuid = AddDbTypeMapping(new(typeof(Guid), [ DbType.Guid ], convert: convertGuid)); AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)", createFormat: "CHAR(36)")); // null - var typeNull = AddDbTypeMapping(new(typeof(object), new[] { DbType.Object })); + var typeNull = AddDbTypeMapping(new(typeof(object), [ DbType.Object ])); AddColumnTypeMetadata(new("NULL", typeNull, MySqlDbType.Null)); } @@ -154,10 +154,15 @@ private void AddColumnTypeMetadata(ColumnTypeMetadata columnTypeMetadata) { m_columnTypeMetadata.Add(columnTypeMetadata); var lookupKey = columnTypeMetadata.CreateLookupKey(); +#if !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER if (!m_columnTypeMetadataLookup.ContainsKey(lookupKey)) m_columnTypeMetadataLookup.Add(lookupKey, columnTypeMetadata); if (!m_mySqlDbTypeToColumnTypeMetadata.ContainsKey(columnTypeMetadata.MySqlDbType)) m_mySqlDbTypeToColumnTypeMetadata.Add(columnTypeMetadata.MySqlDbType, columnTypeMetadata); +#else + m_columnTypeMetadataLookup.TryAdd(lookupKey, columnTypeMetadata); + m_mySqlDbTypeToColumnTypeMetadata.TryAdd(columnTypeMetadata.MySqlDbType, columnTypeMetadata); +#endif } internal DbTypeMapping? GetDbTypeMapping(Type clrType) diff --git a/src/MySqlConnector/Core/XaEnlistedTransaction.cs b/src/MySqlConnector/Core/XaEnlistedTransaction.cs index 69ef49255..9f1ffe73c 100644 --- a/src/MySqlConnector/Core/XaEnlistedTransaction.cs +++ b/src/MySqlConnector/Core/XaEnlistedTransaction.cs @@ -3,13 +3,8 @@ namespace MySqlConnector.Core; -internal sealed class XaEnlistedTransaction : EnlistedTransactionBase +internal sealed class XaEnlistedTransaction(Transaction transaction, MySqlConnection connection) : EnlistedTransactionBase(transaction, connection) { - public XaEnlistedTransaction(Transaction transaction, MySqlConnection connection) - : base(transaction, connection) - { - } - protected override void OnStart() { // generate an "xid" with "gtrid" (Global TRansaction ID) from the .NET Transaction and "bqual" (Branch QUALifier) diff --git a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs index f264ffb54..e2e9a827f 100644 --- a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs +++ b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs @@ -16,15 +16,9 @@ public ConsoleLoggerProvider(MySqlConnectorLogLevel minimumLevel = MySqlConnecto public IMySqlConnectorLogger CreateLogger(string name) => new ConsoleLogger(this, name); - private sealed class ConsoleLogger : IMySqlConnectorLogger + private sealed class ConsoleLogger(ConsoleLoggerProvider provider, string name) : IMySqlConnectorLogger { - public ConsoleLogger(ConsoleLoggerProvider provider, string name) - { - m_provider = provider; - m_name = name; - } - - public bool IsEnabled(MySqlConnectorLogLevel level) => level >= m_provider.m_minimumLevel && level <= MySqlConnectorLogLevel.Fatal; + public bool IsEnabled(MySqlConnectorLogLevel level) => level >= Provider.m_minimumLevel && level <= MySqlConnectorLogLevel.Fatal; public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = null, Exception? exception = null) { @@ -34,7 +28,7 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = var sb = new StringBuilder(); sb.Append(s_levels[(int) level]); sb.Append('\t'); - sb.Append(m_name); + sb.Append(Name); sb.Append('\t'); if (args is null || args.Length == 0) @@ -46,9 +40,9 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = if (exception is not null) sb.AppendLine(exception.ToString()); - if (m_provider.m_isColored) + if (Provider.m_isColored) { - lock (m_provider) + lock (Provider) { var oldColor = Console.ForegroundColor; Console.ForegroundColor = s_colors[(int) level]; @@ -84,8 +78,8 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = ConsoleColor.Red, }; - private readonly ConsoleLoggerProvider m_provider; - private readonly string m_name; + private ConsoleLoggerProvider Provider { get; } = provider; + private string Name { get; } = name; } private readonly MySqlConnectorLogLevel m_minimumLevel; diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs index 9c914425b..2dd3bf9bb 100644 --- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs +++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs @@ -21,40 +21,32 @@ public static IMySqlConnectorLoggerProvider Provider } // A helper class that adapts ILoggerFactory to the old-style IMySqlConnectorLoggerProvider interface. - private sealed class MySqlConnectorLoggerFactor : ILoggerFactory + private sealed class MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) : ILoggerFactory { - public MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) => - m_loggerProvider = loggerProvider; - public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException(); public ILogger CreateLogger(string categoryName) { // assume all logger names start with "MySqlConnector." but the old API didn't expect that prefix - return new MySqlConnectorLogger(m_loggerProvider.CreateLogger(categoryName[15..])); + return new MySqlConnectorLogger(loggerProvider.CreateLogger(categoryName[15..])); } public void Dispose() { } - - private readonly IMySqlConnectorLoggerProvider m_loggerProvider; } // A helper class that adapts ILogger to the old-style IMySqlConnectorLogger interface. - private sealed class MySqlConnectorLogger : ILogger + private sealed class MySqlConnectorLogger(IMySqlConnectorLogger logger) : ILogger { - public MySqlConnectorLogger(IMySqlConnectorLogger logger) => - m_logger = logger; - public IDisposable BeginScope(TState state) where TState : notnull => throw new NotSupportedException(); - public bool IsEnabled(LogLevel logLevel) => m_logger.IsEnabled(ConvertLogLevel(logLevel)); + public bool IsEnabled(LogLevel logLevel) => logger.IsEnabled(ConvertLogLevel(logLevel)); public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => - m_logger.Log(ConvertLogLevel(logLevel), formatter(state, exception), exception: exception); + logger.Log(ConvertLogLevel(logLevel), formatter(state, exception), exception: exception); private static MySqlConnectorLogLevel ConvertLogLevel(LogLevel logLevel) => logLevel switch @@ -67,7 +59,5 @@ private static MySqlConnectorLogLevel ConvertLogLevel(LogLevel logLevel) => LogLevel.Critical => MySqlConnectorLogLevel.Fatal, _ => MySqlConnectorLogLevel.Info, }; - - private readonly IMySqlConnectorLogger m_logger; } } diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs index 294eff95f..0ea00778f 100644 --- a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs +++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs @@ -3,22 +3,13 @@ namespace MySqlConnector.Logging; -internal sealed class MySqlConnectorLoggingConfiguration +internal sealed class MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory) { - public MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory) - { - DataSourceLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource"); - ConnectionLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection"); - CommandLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand"); - PoolLogger = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool"); - BulkCopyLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy"); - } - - public ILogger DataSourceLogger { get; } - public ILogger ConnectionLogger { get; } - public ILogger CommandLogger { get; } - public ILogger PoolLogger { get; } - public ILogger BulkCopyLogger { get; } + public ILogger DataSourceLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource"); + public ILogger ConnectionLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection"); + public ILogger CommandLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand"); + public ILogger PoolLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool"); + public ILogger BulkCopyLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy"); public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance); public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration; diff --git a/src/MySqlConnector/MySqlAttribute.cs b/src/MySqlConnector/MySqlAttribute.cs index 613cd9cf9..5ff72b879 100644 --- a/src/MySqlConnector/MySqlAttribute.cs +++ b/src/MySqlConnector/MySqlAttribute.cs @@ -5,40 +5,31 @@ namespace MySqlConnector /// /// See Query Attributes for information on using query attributes. #pragma warning disable CA1711 // Identifiers should not have incorrect suffix - public sealed class MySqlAttribute : ICloneable + public sealed class MySqlAttribute(string attributeName, object? value) : ICloneable #pragma warning restore CA1711 // Identifiers should not have incorrect suffix { /// /// Initializes a new . /// public MySqlAttribute() + : this("", null) { - AttributeName = ""; - } - - /// - /// Initializes a new with the specified attribute name and value. - /// - public MySqlAttribute(string attributeName, object? value) - { - AttributeName = attributeName ?? ""; - Value = value; } /// /// Gets or sets the attribute name. /// - public string AttributeName { get; set; } + public string AttributeName { get; set; } = attributeName ?? ""; /// /// Gets or sets the attribute value. /// - public object? Value { get; set; } + public object? Value { get; set; } = value; /// /// Returns a new with the same property values as this instance. /// - public MySqlAttribute Clone() => new MySqlAttribute(AttributeName, Value); + public MySqlAttribute Clone() => new(AttributeName, Value); object ICloneable.Clone() => Clone(); diff --git a/src/MySqlConnector/MySqlAttributeCollection.cs b/src/MySqlConnector/MySqlAttributeCollection.cs index ed44a8907..cd7c7c273 100644 --- a/src/MySqlConnector/MySqlAttributeCollection.cs +++ b/src/MySqlConnector/MySqlAttributeCollection.cs @@ -19,8 +19,18 @@ public sealed class MySqlAttributeCollection : IEnumerable /// The attribute name must not be empty, and must not already exist in the collection. public void Add(MySqlAttribute attribute) { - if (string.IsNullOrEmpty((attribute ?? throw new ArgumentNullException(nameof(attribute))).AttributeName)) +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(attribute); +#else + if (attribute is null) + throw new ArgumentNullException(nameof(attribute)); +#endif +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(attribute.AttributeName); +#else + if (string.IsNullOrEmpty(attribute.AttributeName)) throw new ArgumentException("Attribute name must not be empty", nameof(attribute)); +#endif foreach (var existingAttribute in m_attributes) { if (existingAttribute.AttributeName == attribute.AttributeName) @@ -36,8 +46,12 @@ public void Add(MySqlAttribute attribute) /// The attribute value. public void SetAttribute(string attributeName, object? value) { +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(attributeName); +#else if (string.IsNullOrEmpty(attributeName)) throw new ArgumentException("Attribute name must not be empty", nameof(attributeName)); +#endif for (var i = 0; i < m_attributes.Count; i++) { if (m_attributes[i].AttributeName == attributeName) @@ -78,7 +92,7 @@ public void SetAttribute(string attributeName, object? value) /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal MySqlAttributeCollection() => m_attributes = new(); + internal MySqlAttributeCollection() => m_attributes = []; private readonly List m_attributes; } diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs index 913eefdff..1aa0a1731 100644 --- a/src/MySqlConnector/MySqlBatch.cs +++ b/src/MySqlConnector/MySqlBatch.cs @@ -75,7 +75,7 @@ public MySqlBatch(MySqlConnection? connection = null, MySqlTransaction? transact { Connection = connection; Transaction = transaction; - BatchCommands = new(); + BatchCommands = []; m_commandId = ICancellableCommandExtensions.GetNextId(); } @@ -130,6 +130,7 @@ public async Task ExecuteReaderAsync(CancellationToken cancella #if NET6_0_OR_GREATER protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) #else + [SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Matches .NET 6.0 override")] private DbDataReader ExecuteDbDataReader(CommandBehavior behavior) #endif { @@ -362,7 +363,7 @@ private bool NeedsPrepare(out Exception? exception) return exception is null && !Connection!.IgnorePrepare; } - private Exception? GetExceptionForInvalidCommands() + private InvalidOperationException? GetExceptionForInvalidCommands() { foreach (var command in BatchCommands) { diff --git a/src/MySqlConnector/MySqlBatchCommand.cs b/src/MySqlConnector/MySqlBatchCommand.cs index 1fc7393d7..3a08107ee 100644 --- a/src/MySqlConnector/MySqlBatchCommand.cs +++ b/src/MySqlConnector/MySqlBatchCommand.cs @@ -46,7 +46,21 @@ public MySqlBatchCommand(string? commandText) #else public MySqlParameterCollection Parameters => #endif - m_parameterCollection ??= new(); + m_parameterCollection ??= []; + +#pragma warning disable CA1822 // Mark members as static + public +#if NET8_0_OR_GREATER + override +#endif + DbParameter CreateParameter() => new MySqlParameter(); + + public +#if NET8_0_OR_GREATER + override +#endif + bool CanCreateParameter => true; +#pragma warning restore CA1822 // Mark members as static #if NET6_0_OR_GREATER protected override DbParameterCollection DbParameterCollection => Parameters; diff --git a/src/MySqlConnector/MySqlBatchCommandCollection.cs b/src/MySqlConnector/MySqlBatchCommandCollection.cs index 018e59678..e9eb3a04a 100644 --- a/src/MySqlConnector/MySqlBatchCommandCollection.cs +++ b/src/MySqlConnector/MySqlBatchCommandCollection.cs @@ -9,7 +9,7 @@ public sealed class MySqlBatchCommandCollection : IList, ICollection, IEnumerable, IEnumerable #endif { - internal MySqlBatchCommandCollection() => m_commands = new(); + internal MySqlBatchCommandCollection() => m_commands = []; #if NET6_0_OR_GREATER public new MySqlBatchCommand this[int index] { get => (MySqlBatchCommand) base[index]; set => base[index] = value; } diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs index 1babc5dab..88170b243 100644 --- a/src/MySqlConnector/MySqlBulkCopy.cs +++ b/src/MySqlConnector/MySqlBulkCopy.cs @@ -52,10 +52,16 @@ public sealed class MySqlBulkCopy /// (Optional) The to use. public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction = null) { - m_connection = connection ?? throw new ArgumentNullException(nameof(connection)); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(connection); +#else + if (connection is null) + throw new ArgumentNullException(nameof(connection)); +#endif + m_connection = connection; m_transaction = transaction; m_logger = m_connection.LoggingConfiguration.BulkCopyLogger; - ColumnMappings = new(); + ColumnMappings = []; } /// @@ -111,7 +117,13 @@ public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction = /// A with the result of the bulk copy operation. public MySqlBulkCopyResult WriteToServer(DataTable dataTable) { - m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataTable); +#else + if (dataTable is null) + throw new ArgumentNullException(nameof(dataTable)); +#endif + m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable); #pragma warning disable CA2012 // Safe because method completes synchronously return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult(); #pragma warning restore CA2012 @@ -126,7 +138,13 @@ public MySqlBulkCopyResult WriteToServer(DataTable dataTable) /// A with the result of the bulk copy operation. public async ValueTask WriteToServerAsync(DataTable dataTable, CancellationToken cancellationToken = default) { - m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataTable); +#else + if (dataTable is null) + throw new ArgumentNullException(nameof(dataTable)); +#endif + m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable); return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false); } @@ -140,7 +158,13 @@ public async ValueTask WriteToServerAsync(DataTable dataTab /// A with the result of the bulk copy operation. public MySqlBulkCopyResult WriteToServer(IEnumerable dataRows, int columnCount) { - m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataRows); +#else + if (dataRows is null) + throw new ArgumentNullException(nameof(dataRows)); +#endif + m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows, columnCount); #pragma warning disable CA2012 // Safe because method completes synchronously return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult(); #pragma warning restore CA2012 @@ -157,7 +181,13 @@ public MySqlBulkCopyResult WriteToServer(IEnumerable dataRows, int colu /// A with the result of the bulk copy operation. public async ValueTask WriteToServerAsync(IEnumerable dataRows, int columnCount, CancellationToken cancellationToken = default) { - m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataRows); +#else + if (dataRows is null) + throw new ArgumentNullException(nameof(dataRows)); +#endif + m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows, columnCount); return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false); } @@ -169,7 +199,13 @@ public async ValueTask WriteToServerAsync(IEnumerableA with the result of the bulk copy operation. public MySqlBulkCopyResult WriteToServer(IDataReader dataReader) { - m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataReader); +#else + if (dataReader is null) + throw new ArgumentNullException(nameof(dataReader)); +#endif + m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader); #pragma warning disable CA2012 // Safe because method completes synchronously return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult(); #pragma warning restore CA2012 @@ -184,7 +220,13 @@ public MySqlBulkCopyResult WriteToServer(IDataReader dataReader) /// A with the result of the bulk copy operation. public async ValueTask WriteToServerAsync(IDataReader dataReader, CancellationToken cancellationToken = default) { - m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataReader); +#else + if (dataReader is null) + throw new ArgumentNullException(nameof(dataReader)); +#endif + m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader); return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false); } @@ -658,8 +700,8 @@ static bool WriteBytes(ReadOnlySpan value, ref int inputIndex, Span } } - private static readonly char[] s_specialCharacters = new char[] { '\t', '\\', '\n' }; - private static readonly Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + private static readonly char[] s_specialCharacters = [ '\t', '\\', '\n' ]; + private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private readonly MySqlConnection m_connection; private readonly MySqlTransaction? m_transaction; diff --git a/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs b/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs index 9de514437..0a3328707 100644 --- a/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs +++ b/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs @@ -28,38 +28,28 @@ namespace MySqlConnector; /// }, /// /// -public sealed class MySqlBulkCopyColumnMapping +/// The zero-based ordinal position of the source column. +/// The name of the destination column. +/// The optional expression to be used to set the destination column. +public sealed class MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, string? expression = null) { /// /// Initializes with the default values. /// public MySqlBulkCopyColumnMapping() + : this(0, "", null) { - DestinationColumn = ""; - } - - /// - /// Initializes to the specified values. - /// - /// The zero-based ordinal position of the source column. - /// The name of the destination column. - /// The optional expression to be used to set the destination column. - public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, string? expression = null) - { - SourceOrdinal = sourceOrdinal; - DestinationColumn = destinationColumn ?? throw new ArgumentNullException(nameof(destinationColumn)); - Expression = expression; } /// /// The zero-based ordinal position of the source column to map from. /// - public int SourceOrdinal { get; set; } + public int SourceOrdinal { get; set; } = sourceOrdinal; /// /// The name of the destination column to copy to. To use an expression, this should be the name of a unique user-defined variable. /// - public string DestinationColumn { get; set; } + public string DestinationColumn { get; set; } = destinationColumn; /// /// An optional expression for setting a destination column. To use an expression, the should @@ -67,5 +57,5 @@ public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, s /// /// To populate a binary column, you must set to a variable name, and to an /// expression that uses UNHEX to set the column value, e.g., `destColumn` = UNHEX(@variableName). - public string? Expression { get; set; } + public string? Expression { get; set; } = expression; } diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs index 45aff9ac4..1ab11c8fc 100644 --- a/src/MySqlConnector/MySqlBulkLoader.cs +++ b/src/MySqlConnector/MySqlBulkLoader.cs @@ -134,8 +134,8 @@ public MySqlBulkLoader(MySqlConnection connection) { Connection = connection; Local = true; - Columns = new(); - Expressions = new(); + Columns = []; + Expressions = []; } /// @@ -281,7 +281,7 @@ private string CreateSql() return sb.ToString(); } - private static Stream CreateFileStream(string fileName) + private static FileStream CreateFileStream(string fileName) { try { @@ -320,5 +320,5 @@ internal static bool TryGetAndRemoveSource(string sourceKey, [NotNullWhen(true)] private static string GenerateSourceFileName() => SourcePrefix + Guid.NewGuid().ToString("N"); private static readonly object s_lock = new(); - private static readonly Dictionary s_sources = new(); + private static readonly Dictionary s_sources = []; } diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs index 5cdf1082a..d8a124ad8 100644 --- a/src/MySqlConnector/MySqlCommand.cs +++ b/src/MySqlConnector/MySqlCommand.cs @@ -81,14 +81,14 @@ private MySqlCommand(MySqlCommand other) /// /// The collection of objects for this command. /// - public new MySqlParameterCollection Parameters => m_parameterCollection ??= new(); + public new MySqlParameterCollection Parameters => m_parameterCollection ??= []; MySqlParameterCollection? IMySqlCommand.RawParameters => m_parameterCollection; /// /// The collection of objects for this command. /// - public MySqlAttributeCollection Attributes => m_attributeCollection ??= new(); + public MySqlAttributeCollection Attributes => m_attributeCollection ??= []; MySqlAttributeCollection? IMySqlCommand.RawAttributes => m_attributeCollection; @@ -230,7 +230,13 @@ public override int CommandTimeout get => Math.Min(m_commandTimeout ?? Connection?.DefaultCommandTimeout ?? 0, int.MaxValue / 1000); set { - m_commandTimeout = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero."); +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(value); +#else + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero."); +#endif + m_commandTimeout = value; ((ICancellableCommand) this).EffectiveCommandTimeout = null; } } diff --git a/src/MySqlConnector/MySqlCommandBuilder.cs b/src/MySqlConnector/MySqlCommandBuilder.cs index 5a9d361e8..b69b9444d 100644 --- a/src/MySqlConnector/MySqlCommandBuilder.cs +++ b/src/MySqlConnector/MySqlCommandBuilder.cs @@ -12,8 +12,12 @@ public sealed class MySqlCommandBuilder : DbCommandBuilder private static async Task DeriveParametersAsync(IOBehavior ioBehavior, MySqlCommand command, CancellationToken cancellationToken) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(command); +#else if (command is null) throw new ArgumentNullException(nameof(command)); +#endif if (command.CommandType != CommandType.StoredProcedure) throw new ArgumentException($"MySqlCommand.CommandType must be StoredProcedure not {command.CommandType}", nameof(command)); if (string.IsNullOrWhiteSpace(command.CommandText)) diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs index b5802327d..ddb259522 100644 --- a/src/MySqlConnector/MySqlConnection.cs +++ b/src/MySqlConnector/MySqlConnection.cs @@ -213,7 +213,7 @@ public override void EnlistTransaction(System.Transactions.Transaction? transact lock (s_lock) { if (!s_transactionConnections.TryGetValue(transaction, out var enlistedTransactions)) - s_transactionConnections[transaction] = enlistedTransactions = new(); + s_transactionConnections[transaction] = enlistedTransactions = []; enlistedTransactions.Add(m_enlistedTransaction); } } @@ -294,8 +294,12 @@ internal void UnenlistTransaction() private void TakeSessionFrom(MySqlConnection other) { #if DEBUG +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(other); +#else if (other is null) throw new ArgumentNullException(nameof(other)); +#endif if (m_session is not null) throw new InvalidOperationException("This connection must not have a session"); if (other.m_session is null) @@ -555,8 +559,12 @@ public override string ConnectionString private static async Task ClearPoolAsync(MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(connection); +#else if (connection is null) throw new ArgumentNullException(nameof(connection)); +#endif var pool = ConnectionPool.GetPool(connection.m_connectionString, null, createIfNotFound: false); if (pool is not null) @@ -791,7 +799,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel) if (cachedProcedures is null) { Log.PoolDoesNotHaveSharedProcedureCache(m_logger, m_session.Id, m_session.Pool?.Id); - cachedProcedures = m_cachedProcedures = new(); + cachedProcedures = m_cachedProcedures = []; } var normalized = NormalizedSchema.MustNormalize(name, Database); @@ -855,8 +863,12 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel) internal void SetActiveReader(MySqlDataReader dataReader) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(dataReader); +#else if (dataReader is null) throw new ArgumentNullException(nameof(dataReader)); +#endif if (m_activeReader is not null) throw new InvalidOperationException("Can't replace active reader."); m_activeReader = dataReader; @@ -979,8 +991,12 @@ private MySqlConnection(MySqlConnection other, MySqlDataSource? dataSource, stri private void VerifyNotDisposed() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(m_isDisposed, this); +#else if (m_isDisposed) throw new ObjectDisposedException(GetType().Name); +#endif } private async Task CloseAsync(bool changeState, IOBehavior ioBehavior) @@ -1097,7 +1113,7 @@ private ConnectionSettings GetConnectionSettings() => private static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new(ConnectionState.Connecting, ConnectionState.Open); private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed); private static readonly object s_lock = new(); - private static readonly Dictionary> s_transactionConnections = new(); + private static readonly Dictionary> s_transactionConnections = []; private readonly MySqlDataSource? m_dataSource; private readonly ILogger m_logger; diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs index b99bad560..b82352596 100644 --- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs +++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs @@ -1,4 +1,7 @@ using System.Collections; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -887,7 +890,7 @@ protected override void GetProperties(Hashtable propertyDescriptors) internal abstract partial class MySqlConnectionStringOption { - public static List OptionNames { get; } = new(); + public static List OptionNames { get; } = []; // Connection Options public static readonly MySqlConnectionStringReferenceOption Server; @@ -970,10 +973,10 @@ protected MySqlConnectionStringOption(IReadOnlyList keys) m_keys = keys; } - private static void AddOption(MySqlConnectionStringOption option) + private static void AddOption(Dictionary options, MySqlConnectionStringOption option) { - foreach (string key in option.m_keys) - s_options.Add(key, option); + foreach (var key in option.m_keys) + options.Add(key, option); OptionNames.Add(option.m_keys[0]); } @@ -981,76 +984,77 @@ private static void AddOption(MySqlConnectionStringOption option) #pragma warning disable CA1810 // Initialize reference type static fields inline static MySqlConnectionStringOption() { - s_options = new(StringComparer.OrdinalIgnoreCase); + var options = new Dictionary(StringComparer.OrdinalIgnoreCase); // Base Options - AddOption(Server = new( - keys: new[] { "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" }, +#pragma warning disable SA1118 // Parameter should not span multiple lines + AddOption(options, Server = new( + keys: [ "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" ], defaultValue: "")); - AddOption(Port = new( - keys: new[] { "Port" }, + AddOption(options, Port = new( + keys: [ "Port" ], defaultValue: 3306u)); - AddOption(UserID = new( - keys: new[] { "User ID", "UserID", "Username", "Uid", "User name", "User" }, + AddOption(options, UserID = new( + keys: [ "User ID", "UserID", "Username", "Uid", "User name", "User" ], defaultValue: "")); - AddOption(Password = new( - keys: new[] { "Password", "pwd" }, + AddOption(options, Password = new( + keys: [ "Password", "pwd" ], defaultValue: "")); - AddOption(Database = new( - keys: new[] { "Database", "Initial Catalog" }, + AddOption(options, Database = new( + keys: [ "Database", "Initial Catalog" ], defaultValue: "")); - AddOption(LoadBalance = new( - keys: new[] { "Load Balance", "LoadBalance" }, + AddOption(options, LoadBalance = new( + keys: [ "Load Balance", "LoadBalance" ], defaultValue: MySqlLoadBalance.RoundRobin)); - AddOption(ConnectionProtocol = new( - keys: new[] { "Connection Protocol", "ConnectionProtocol", "Protocol" }, + AddOption(options, ConnectionProtocol = new( + keys: [ "Connection Protocol", "ConnectionProtocol", "Protocol" ], defaultValue: MySqlConnectionProtocol.Socket)); - AddOption(PipeName = new( - keys: new[] { "Pipe Name", "PipeName", "Pipe" }, + AddOption(options, PipeName = new( + keys: [ "Pipe Name", "PipeName", "Pipe" ], defaultValue: "MYSQL")); // SSL/TLS Options - AddOption(SslMode = new( - keys: new[] { "SSL Mode", "SslMode" }, + AddOption(options, SslMode = new( + keys: [ "SSL Mode", "SslMode" ], defaultValue: MySqlSslMode.Preferred)); - AddOption(CertificateFile = new( - keys: new[] { "Certificate File", "CertificateFile" }, + AddOption(options, CertificateFile = new( + keys: [ "Certificate File", "CertificateFile" ], defaultValue: "")); - AddOption(CertificatePassword = new( - keys: new[] { "Certificate Password", "CertificatePassword" }, + AddOption(options, CertificatePassword = new( + keys: [ "Certificate Password", "CertificatePassword" ], defaultValue: "")); - AddOption(CertificateStoreLocation = new( - keys: new[] { "Certificate Store Location", "CertificateStoreLocation" }, + AddOption(options, CertificateStoreLocation = new( + keys: [ "Certificate Store Location", "CertificateStoreLocation" ], defaultValue: MySqlCertificateStoreLocation.None)); - AddOption(CertificateThumbprint = new( - keys: new[] { "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" }, + AddOption(options, CertificateThumbprint = new( + keys: [ "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" ], defaultValue: "")); - AddOption(SslCert = new( - keys: new[] { "SSL Cert", "SslCert", "Ssl-Cert" }, + AddOption(options, SslCert = new( + keys: [ "SSL Cert", "SslCert", "Ssl-Cert" ], defaultValue: "")); - AddOption(SslKey = new( - keys: new[] { "SSL Key", "SslKey", "Ssl-Key" }, + AddOption(options, SslKey = new( + keys: [ "SSL Key", "SslKey", "Ssl-Key" ], defaultValue: "")); - AddOption(SslCa = new( - keys: new[] { "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" }, + AddOption(options, SslCa = new( + keys: [ "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" ], defaultValue: "")); - AddOption(TlsVersion = new( - keys: new[] { "TLS Version", "TlsVersion", "Tls-Version" }, + AddOption(options, TlsVersion = new( + keys: [ "TLS Version", "TlsVersion", "Tls-Version" ], defaultValue: "", coerce: value => { @@ -1092,74 +1096,74 @@ static MySqlConnectionStringOption() return coercedValue; })); - AddOption(TlsCipherSuites = new( - keys: new[] { "TLS Cipher Suites", "TlsCipherSuites" }, + AddOption(options, TlsCipherSuites = new( + keys: [ "TLS Cipher Suites", "TlsCipherSuites" ], defaultValue: "")); // Connection Pooling Options - AddOption(Pooling = new( - keys: new[] { "Pooling" }, + AddOption(options, Pooling = new( + keys: [ "Pooling" ], defaultValue: true)); - AddOption(ConnectionLifeTime = new( - keys: new[] { "Connection Lifetime", "ConnectionLifeTime" }, + AddOption(options, ConnectionLifeTime = new( + keys: [ "Connection Lifetime", "ConnectionLifeTime" ], defaultValue: 0u)); - AddOption(ConnectionReset = new( - keys: new[] { "Connection Reset", "ConnectionReset" }, + AddOption(options, ConnectionReset = new( + keys: [ "Connection Reset", "ConnectionReset" ], defaultValue: true)); - AddOption(DeferConnectionReset = new( - keys: new[] { "Defer Connection Reset", "DeferConnectionReset" }, + AddOption(options, DeferConnectionReset = new( + keys: [ "Defer Connection Reset", "DeferConnectionReset" ], defaultValue: true)); - AddOption(ConnectionIdlePingTime = new( - keys: new[] { "Connection Idle Ping Time", "ConnectionIdlePingTime" }, + AddOption(options, ConnectionIdlePingTime = new( + keys: [ "Connection Idle Ping Time", "ConnectionIdlePingTime" ], defaultValue: 0u)); - AddOption(ConnectionIdleTimeout = new( - keys: new[] { "Connection Idle Timeout", "ConnectionIdleTimeout" }, + AddOption(options, ConnectionIdleTimeout = new( + keys: [ "Connection Idle Timeout", "ConnectionIdleTimeout" ], defaultValue: 180u)); - AddOption(MinimumPoolSize = new( - keys: new[] { "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" }, + AddOption(options, MinimumPoolSize = new( + keys: [ "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" ], defaultValue: 0u)); - AddOption(MaximumPoolSize = new( - keys: new[] { "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" }, + AddOption(options, MaximumPoolSize = new( + keys: [ "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" ], defaultValue: 100u)); - AddOption(DnsCheckInterval = new( - keys: new[] { "DNS Check Interval", "DnsCheckInterval" }, + AddOption(options, DnsCheckInterval = new( + keys: [ "DNS Check Interval", "DnsCheckInterval" ], defaultValue: 0u)); // Other Options - AddOption(AllowLoadLocalInfile = new( - keys: new[] { "Allow Load Local Infile", "AllowLoadLocalInfile" }, + AddOption(options, AllowLoadLocalInfile = new( + keys: [ "Allow Load Local Infile", "AllowLoadLocalInfile" ], defaultValue: false)); - AddOption(AllowPublicKeyRetrieval = new( - keys: new[] { "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" }, + AddOption(options, AllowPublicKeyRetrieval = new( + keys: [ "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" ], defaultValue: false)); - AddOption(AllowUserVariables = new( - keys: new[] { "Allow User Variables", "AllowUserVariables" }, + AddOption(options, AllowUserVariables = new( + keys: [ "Allow User Variables", "AllowUserVariables" ], defaultValue: false)); - AddOption(AllowZeroDateTime = new( - keys: new[] { "Allow Zero DateTime", "AllowZeroDateTime" }, + AddOption(options, AllowZeroDateTime = new( + keys: [ "Allow Zero DateTime", "AllowZeroDateTime" ], defaultValue: false)); - AddOption(ApplicationName = new( - keys: new[] { "Application Name", "ApplicationName" }, + AddOption(options, ApplicationName = new( + keys: [ "Application Name", "ApplicationName" ], defaultValue: "")); - AddOption(AutoEnlist = new( - keys: new[] { "Auto Enlist", "AutoEnlist" }, + AddOption(options, AutoEnlist = new( + keys: [ "Auto Enlist", "AutoEnlist" ], defaultValue: true)); - AddOption(CancellationTimeout = new( - keys: new[] { "Cancellation Timeout", "CancellationTimeout" }, + AddOption(options, CancellationTimeout = new( + keys: [ "Cancellation Timeout", "CancellationTimeout" ], defaultValue: 2, coerce: x => { @@ -1168,93 +1172,100 @@ static MySqlConnectionStringOption() return x; })); - AddOption(CharacterSet = new( - keys: new[] { "Character Set", "CharSet", "CharacterSet" }, + AddOption(options, CharacterSet = new( + keys: [ "Character Set", "CharSet", "CharacterSet" ], defaultValue: "")); - AddOption(ConnectionTimeout = new( - keys: new[] { "Connection Timeout", "ConnectionTimeout", "Connect Timeout" }, + AddOption(options, ConnectionTimeout = new( + keys: [ "Connection Timeout", "ConnectionTimeout", "Connect Timeout" ], defaultValue: 15u)); - AddOption(ConvertZeroDateTime = new( - keys: new[] { "Convert Zero DateTime", "ConvertZeroDateTime" }, + AddOption(options, ConvertZeroDateTime = new( + keys: [ "Convert Zero DateTime", "ConvertZeroDateTime" ], defaultValue: false)); - AddOption(DateTimeKind = new( - keys: new[] { "DateTime Kind", "DateTimeKind" }, + AddOption(options, DateTimeKind = new( + keys: [ "DateTime Kind", "DateTimeKind" ], defaultValue: MySqlDateTimeKind.Unspecified)); - AddOption(DefaultCommandTimeout = new( - keys: new[] { "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" }, + AddOption(options, DefaultCommandTimeout = new( + keys: [ "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" ], defaultValue: 30u)); - AddOption(ForceSynchronous = new( - keys: new[] { "Force Synchronous", "ForceSynchronous" }, + AddOption(options, ForceSynchronous = new( + keys: [ "Force Synchronous", "ForceSynchronous" ], defaultValue: false)); - AddOption(GuidFormat = new( - keys: new[] { "GUID Format", "GuidFormat" }, + AddOption(options, GuidFormat = new( + keys: [ "GUID Format", "GuidFormat" ], defaultValue: MySqlGuidFormat.Default)); - AddOption(IgnoreCommandTransaction = new( - keys: new[] { "Ignore Command Transaction", "IgnoreCommandTransaction" }, + AddOption(options, IgnoreCommandTransaction = new( + keys: [ "Ignore Command Transaction", "IgnoreCommandTransaction" ], defaultValue: false)); - AddOption(IgnorePrepare = new( - keys: new[] { "Ignore Prepare", "IgnorePrepare" }, + AddOption(options, IgnorePrepare = new( + keys: [ "Ignore Prepare", "IgnorePrepare" ], defaultValue: false)); - AddOption(InteractiveSession = new( - keys: new[] { "Interactive Session", "InteractiveSession", "Interactive" }, + AddOption(options, InteractiveSession = new( + keys: [ "Interactive Session", "InteractiveSession", "Interactive" ], defaultValue: false)); - AddOption(Keepalive = new( - keys: new[] { "Keep Alive", "Keepalive" }, + AddOption(options, Keepalive = new( + keys: [ "Keep Alive", "Keepalive" ], defaultValue: 0u)); - AddOption(NoBackslashEscapes = new( - keys: new[] { "No Backslash Escapes", "NoBackslashEscapes" }, + AddOption(options, NoBackslashEscapes = new( + keys: [ "No Backslash Escapes", "NoBackslashEscapes" ], defaultValue: false)); - AddOption(OldGuids = new( - keys: new[] { "Old Guids", "OldGuids" }, + AddOption(options, OldGuids = new( + keys: [ "Old Guids", "OldGuids" ], defaultValue: false)); - AddOption(PersistSecurityInfo = new( - keys: new[] { "Persist Security Info", "PersistSecurityInfo" }, + AddOption(options, PersistSecurityInfo = new( + keys: [ "Persist Security Info", "PersistSecurityInfo" ], defaultValue: false)); - AddOption(Pipelining = new( - keys: new[] { "Pipelining" }, + AddOption(options, Pipelining = new( + keys: [ "Pipelining" ], defaultValue: true)); - AddOption(ServerRedirectionMode = new( - keys: new[] { "Server Redirection Mode", "ServerRedirectionMode" }, + AddOption(options, ServerRedirectionMode = new( + keys: [ "Server Redirection Mode", "ServerRedirectionMode" ], defaultValue: MySqlServerRedirectionMode.Disabled)); - AddOption(ServerRsaPublicKeyFile = new( - keys: new[] { "Server RSA Public Key File", "ServerRsaPublicKeyFile" }, + AddOption(options, ServerRsaPublicKeyFile = new( + keys: [ "Server RSA Public Key File", "ServerRsaPublicKeyFile" ], defaultValue: "")); - AddOption(ServerSPN = new( - keys: new[] { "Server SPN", "ServerSPN" }, + AddOption(options, ServerSPN = new( + keys: [ "Server SPN", "ServerSPN" ], defaultValue: "")); - AddOption(TreatTinyAsBoolean = new( - keys: new[] { "Treat Tiny As Boolean", "TreatTinyAsBoolean" }, + AddOption(options, TreatTinyAsBoolean = new( + keys: [ "Treat Tiny As Boolean", "TreatTinyAsBoolean" ], defaultValue: true)); - AddOption(UseAffectedRows = new( - keys: new[] { "Use Affected Rows", "UseAffectedRows" }, + AddOption(options, UseAffectedRows = new( + keys: [ "Use Affected Rows", "UseAffectedRows" ], defaultValue: false)); - AddOption(UseCompression = new( - keys: new[] { "Use Compression", "Compress", "UseCompression" }, + AddOption(options, UseCompression = new( + keys: [ "Use Compression", "Compress", "UseCompression" ], defaultValue: false)); - AddOption(UseXaTransactions = new( - keys: new[] { "Use XA Transactions", "UseXaTransactions" }, + AddOption(options, UseXaTransactions = new( + keys: [ "Use XA Transactions", "UseXaTransactions" ], defaultValue: true)); +#pragma warning restore SA1118 // Parameter should not span multiple lines + +#if NET8_0_OR_GREATER + s_options = options.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); +#else + s_options = options; +#endif } private const string c_tlsVersionsRegexPattern = @"\s*TLS( ?v?(1|1\.?0|1\.?1|1\.?2|1\.?3))?$"; @@ -1265,7 +1276,11 @@ static MySqlConnectionStringOption() private static Regex TlsVersionsRegex() => s_tlsVersionsRegex; private static readonly Regex s_tlsVersionsRegex = new(c_tlsVersionsRegexPattern, RegexOptions.IgnoreCase); #endif +#if NET8_0_OR_GREATER + private static readonly FrozenDictionary s_options; +#else private static readonly Dictionary s_options; +#endif private readonly IReadOnlyList m_keys; } diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj index bf3a491bd..a8e3df037 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 + net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.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–2023 Bradley Grainger Bradley Grainger @@ -16,7 +16,7 @@ - + @@ -25,12 +25,12 @@ - - + + - + @@ -41,7 +41,7 @@ - + diff --git a/src/MySqlConnector/MySqlConversionException.cs b/src/MySqlConnector/MySqlConversionException.cs index 130c3cc54..1e8a4ad67 100644 --- a/src/MySqlConnector/MySqlConversionException.cs +++ b/src/MySqlConnector/MySqlConversionException.cs @@ -17,6 +17,9 @@ internal MySqlConversionException(string message) { } +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private MySqlConversionException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/src/MySqlConnector/MySqlDataAdapter.cs b/src/MySqlConnector/MySqlDataAdapter.cs index ba67a5f88..f9a66d0c7 100644 --- a/src/MySqlConnector/MySqlDataAdapter.cs +++ b/src/MySqlConnector/MySqlDataAdapter.cs @@ -202,7 +202,7 @@ public InsertSqlParser(IMySqlCommand command) { CommandText = command.CommandText!; m_parameters = command.RawParameters; - ParameterIndexes = new(); + ParameterIndexes = []; } public List ParameterIndexes { get; } diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs index cde036039..4bdc9c8ac 100644 --- a/src/MySqlConnector/MySqlDataReader.cs +++ b/src/MySqlConnector/MySqlDataReader.cs @@ -347,7 +347,7 @@ public ReadOnlyCollection GetColumnSchema() { var hasNoSchema = !m_resultSet.HasResultSet || m_resultSet.ContainsCommandParameters; if (hasNoSchema) - return new ReadOnlyCollection(Array.Empty()); + return new ReadOnlyCollection([]); var columnDefinitions = m_resultSet.ColumnDefinitions; var resultSet = GetResultSet(); diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs index c3499ef0e..25a34d431 100644 --- a/src/MySqlConnector/MySqlDataSource.cs +++ b/src/MySqlConnector/MySqlDataSource.cs @@ -128,8 +128,12 @@ public string Password protected override DbConnection CreateDbConnection() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(m_isDisposed, this); +#else if (m_isDisposed) throw new ObjectDisposedException(nameof(MySqlDataSource)); +#endif return new MySqlConnection(this) { ProvideClientCertificatesCallback = m_clientCertificatesCallback, diff --git a/src/MySqlConnector/MySqlDateTime.cs b/src/MySqlConnector/MySqlDateTime.cs index f468842d3..909880506 100644 --- a/src/MySqlConnector/MySqlDateTime.cs +++ b/src/MySqlConnector/MySqlDateTime.cs @@ -7,29 +7,15 @@ namespace MySqlConnector; /// as 0000-00-00 that can be stored in MySQL (when /// is true) but can't be stored in a value. /// -public struct MySqlDateTime : IComparable, IComparable, IConvertible, IEquatable +/// The year. +/// The (one-based) month. +/// The (one-based) day of the month. +/// The hour. +/// The minute. +/// The second. +/// The microsecond. +public struct MySqlDateTime(int year, int month, int day, int hour, int minute, int second, int microsecond) : IComparable, IComparable, IConvertible, IEquatable { - /// - /// Initializes a new instance of . - /// - /// The year. - /// The (one-based) month. - /// The (one-based) day of the month. - /// The hour. - /// The minute. - /// The second. - /// The microsecond. - public MySqlDateTime(int year, int month, int day, int hour, int minute, int second, int microsecond) - { - Year = year; - Month = month; - Day = day; - Hour = hour; - Minute = minute; - Second = second; - Microsecond = microsecond; - } - /// /// Initializes a new instance of from a . /// @@ -44,14 +30,8 @@ public MySqlDateTime(DateTime dt) /// /// The whose values will be copied. public MySqlDateTime(MySqlDateTime other) + : this(other.Year, other.Month, other.Day, other.Hour, other.Minute, other.Second, other.Microsecond) { - Year = other.Year; - Month = other.Month; - Day = other.Day; - Hour = other.Hour; - Minute = other.Minute; - Second = other.Second; - Microsecond = other.Microsecond; } /// @@ -62,37 +42,37 @@ public MySqlDateTime(MySqlDateTime other) /// /// Gets or sets the year. /// - public int Year { get; set; } + public int Year { get; set; } = year; /// /// Gets or sets the month. /// - public int Month { get; set; } + public int Month { get; set; } = month; /// /// Gets or sets the day of the month. /// - public int Day { get; set; } + public int Day { get; set; } = day; /// /// Gets or sets the hour. /// - public int Hour { get; set; } + public int Hour { get; set; } = hour; /// /// Gets or sets the minute. /// - public int Minute { get; set; } + public int Minute { get; set; } = minute; /// /// Gets or sets the second. /// - public int Second { get; set; } + public int Second { get; set; } = second; /// /// Gets or sets the microseconds. /// - public int Microsecond { get; set; } + public int Microsecond { get; set; } = microsecond; /// /// Gets or sets the milliseconds. diff --git a/src/MySqlConnector/MySqlException.cs b/src/MySqlConnector/MySqlException.cs index 89c9aa5e1..0032f7a7e 100644 --- a/src/MySqlConnector/MySqlException.cs +++ b/src/MySqlConnector/MySqlException.cs @@ -39,6 +39,9 @@ public sealed class MySqlException : DbException public bool IsTransient => IsErrorTransient(ErrorCode); #endif +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private MySqlException(SerializationInfo info, StreamingContext context) : base(info, context) { @@ -52,6 +55,9 @@ private MySqlException(SerializationInfo info, StreamingContext context) /// /// The that will be set. /// The context. +#if NET8_0_OR_GREATER + [Obsolete("Do not use legacy serialization infrastructure APIs", DiagnosticId = "SYSLIB0051")] +#endif public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); diff --git a/src/MySqlConnector/MySqlHelper.cs b/src/MySqlConnector/MySqlHelper.cs index a5fcdb09b..e78090a9c 100644 --- a/src/MySqlConnector/MySqlHelper.cs +++ b/src/MySqlConnector/MySqlHelper.cs @@ -12,8 +12,12 @@ public sealed class MySqlHelper /// public static string EscapeString(string value) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(value); +#else if (value is null) throw new ArgumentNullException(nameof(value)); +#endif StringBuilder? sb = null; int last = -1; diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs index 148f3edcb..e9a9b8800 100644 --- a/src/MySqlConnector/MySqlParameter.cs +++ b/src/MySqlConnector/MySqlParameter.cs @@ -4,6 +4,9 @@ using System.Globalization; using System.Numerics; using System.Text; +#if NET8_0_OR_GREATER +using System.Text.Unicode; +#endif using MySqlConnector.Core; using MySqlConnector.Protocol.Serialization; using MySqlConnector.Utilities; @@ -176,7 +179,13 @@ private MySqlParameter(MySqlParameter other) private MySqlParameter(MySqlParameter other, string parameterName) : this(other) { - ParameterName = parameterName ?? throw new ArgumentNullException(nameof(parameterName)); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(parameterName); +#else + if (parameterName is null) + throw new ArgumentNullException(nameof(parameterName)); +#endif + ParameterName = parameterName; } internal bool HasSetDirection => m_direction.HasValue; @@ -242,7 +251,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions } else if (Value is decimal decimalValue) { +#if NET8_0_OR_GREATER + decimalValue.TryFormat(writer.GetSpan(31), out var bytesWritten, default, CultureInfo.InvariantCulture); + writer.Advance(bytesWritten); +#else writer.WriteAscii(decimalValue.ToString(CultureInfo.InvariantCulture)); +#endif } else if (Value is short shortValue) { @@ -316,13 +330,23 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions } else if (Value is float floatValue) { +#if NET8_0_OR_GREATER + floatValue.TryFormat(writer.GetSpan(14), out var bytesWritten, "R", CultureInfo.InvariantCulture); + writer.Advance(bytesWritten); +#else // NOTE: Utf8Formatter doesn't support "R" writer.WriteAscii(floatValue.ToString("R", CultureInfo.InvariantCulture)); +#endif } else if (Value is double doubleValue) { +#if NET8_0_OR_GREATER + doubleValue.TryFormat(writer.GetSpan(24), out var bytesWritten, "R", CultureInfo.InvariantCulture); + writer.Advance(bytesWritten); +#else // NOTE: Utf8Formatter doesn't support "R" writer.WriteAscii(doubleValue.ToString("R", CultureInfo.InvariantCulture)); +#endif } else if (Value is BigInteger bigInteger) { @@ -336,12 +360,17 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions { if (mySqlDateTimeValue.IsValidDateTime) { +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten); + writer.Advance(bytesWritten); +#else #if NET6_0_OR_GREATER var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #else var str = FormattableString.Invariant($"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #endif writer.WriteAscii(str); +#endif } else { @@ -351,7 +380,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions #if NET6_0_OR_GREATER else if (Value is DateOnly dateOnlyValue) { +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(23), CultureInfo.InvariantCulture, $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')", out var bytesWritten); + writer.Advance(bytesWritten); +#else writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')")); +#endif } #endif else if (Value is DateTime dateTimeValue) @@ -361,27 +395,42 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions else if ((options & StatementPreparerOptions.DateTimeLocal) != 0 && dateTimeValue.Kind == DateTimeKind.Utc) throw new MySqlException($"DateTime.Kind must not be Utc when DateTimeKind setting is Local (parameter name: {ParameterName})"); +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten); + writer.Advance(bytesWritten); +#else #if NET6_0_OR_GREATER var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #else var str = FormattableString.Invariant($"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #endif writer.WriteAscii(str); +#endif } else if (Value is DateTimeOffset dateTimeOffsetValue) { // store as UTC as it will be read as such when deserialized from a timespan column +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten); + writer.Advance(bytesWritten); +#else #if NET6_0_OR_GREATER var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #else var str = FormattableString.Invariant($"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')"); #endif writer.WriteAscii(str); +#endif } #if NET6_0_OR_GREATER else if (Value is TimeOnly timeOnlyValue) { +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(22), CultureInfo.InvariantCulture, $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'", out var bytesWritten); + writer.Advance(bytesWritten); +#else writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'")); +#endif } #endif else if (Value is TimeSpan ts) @@ -392,12 +441,17 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions writer.Write((byte) '-'); ts = TimeSpan.FromTicks(-ts.Ticks); } +#if NET8_0_OR_GREATER + Utf8.TryWrite(writer.GetSpan(17), CultureInfo.InvariantCulture, $"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'", out var bytesWritten); + writer.Advance(bytesWritten); +#else #if NET6_0_OR_GREATER var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[17], $"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'"); #else var str = FormattableString.Invariant($"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'"); #endif writer.WriteAscii(str); +#endif } else if (Value is Guid guidValue) { @@ -406,16 +460,22 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions { #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER Span bytes = stackalloc byte[16]; +#if NET8_0_OR_GREATER + guidValue.TryWriteBytes(bytes, bigEndian: guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16, out _); +#else guidValue.TryWriteBytes(bytes); +#endif #else var bytes = guidValue.ToByteArray(); #endif if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16) { +#if !NET8_0_OR_GREATER Utility.SwapBytes(bytes, 0, 3); Utility.SwapBytes(bytes, 1, 2); Utility.SwapBytes(bytes, 4, 5); Utility.SwapBytes(bytes, 6, 7); +#endif if (guidOptions == StatementPreparerOptions.GuidFormatTimeSwapBinary16) { @@ -717,16 +777,22 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt { #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER Span bytes = stackalloc byte[16]; +#if NET8_0_OR_GREATER + guidValue.TryWriteBytes(bytes, bigEndian: guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16, out _); +#else guidValue.TryWriteBytes(bytes); +#endif #else var bytes = guidValue.ToByteArray(); #endif if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16) { +#if !NET8_0_OR_GREATER Utility.SwapBytes(bytes, 0, 3); Utility.SwapBytes(bytes, 1, 2); Utility.SwapBytes(bytes, 4, 5); Utility.SwapBytes(bytes, 6, 7); +#endif if (guidOptions == StatementPreparerOptions.GuidFormatTimeSwapBinary16) { diff --git a/src/MySqlConnector/MySqlParameterCollection.cs b/src/MySqlConnector/MySqlParameterCollection.cs index 655778dc6..e9012d37e 100644 --- a/src/MySqlConnector/MySqlParameterCollection.cs +++ b/src/MySqlConnector/MySqlParameterCollection.cs @@ -8,7 +8,7 @@ public sealed class MySqlParameterCollection : DbParameterCollection, IEnumerabl { internal MySqlParameterCollection() { - m_parameters = new(); + m_parameters = []; m_nameToIndex = new(StringComparer.OrdinalIgnoreCase); } @@ -25,13 +25,25 @@ public MySqlParameter Add(string parameterName, DbType dbType) public override int Add(object value) { - AddParameter((MySqlParameter) (value ?? throw new ArgumentNullException(nameof(value))), m_parameters.Count); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(value); +#else + if (value is null) + throw new ArgumentNullException(nameof(value)); +#endif + AddParameter((MySqlParameter) value, m_parameters.Count); return m_parameters.Count - 1; } public MySqlParameter Add(MySqlParameter parameter) { - AddParameter(parameter ?? throw new ArgumentNullException(nameof(parameter)), m_parameters.Count); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(parameter); +#else + if (parameter is null) + throw new ArgumentNullException(nameof(parameter)); +#endif + AddParameter(parameter, m_parameters.Count); return parameter; } @@ -123,7 +135,13 @@ public override void RemoveAt(int index) protected override void SetParameter(int index, DbParameter value) { - var newParameter = (MySqlParameter) (value ?? throw new ArgumentNullException(nameof(value))); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(value); +#else + if (value is null) + throw new ArgumentNullException(nameof(value)); +#endif + var newParameter = (MySqlParameter) value; var oldParameter = m_parameters[index]; if (oldParameter.NormalizedParameterName is not null) m_nameToIndex.Remove(oldParameter.NormalizedParameterName); diff --git a/src/MySqlConnector/MySqlProtocolException.cs b/src/MySqlConnector/MySqlProtocolException.cs index dd51a6e76..f4f4ffbfd 100644 --- a/src/MySqlConnector/MySqlProtocolException.cs +++ b/src/MySqlConnector/MySqlProtocolException.cs @@ -17,6 +17,9 @@ public sealed class MySqlProtocolException : InvalidOperationException internal static MySqlProtocolException CreateForPacketOutOfOrder(int expectedSequenceNumber, int packetSequenceNumber) => new MySqlProtocolException($"Packet received out-of-order. Expected {expectedSequenceNumber:d}; got {packetSequenceNumber:d}."); +#if NET8_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private MySqlProtocolException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/src/MySqlConnector/MySqlTransaction.cs b/src/MySqlConnector/MySqlTransaction.cs index fffd938c6..7039da08a 100644 --- a/src/MySqlConnector/MySqlTransaction.cs +++ b/src/MySqlConnector/MySqlTransaction.cs @@ -149,10 +149,18 @@ private async Task ExecuteSavepointAsync(string command, string savepointName, I { VerifyValid(); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(savepointName); +#else if (savepointName is null) throw new ArgumentNullException(nameof(savepointName)); +#endif +#if NET8_0_OR_GREATER + ArgumentException.ThrowIfNullOrEmpty(savepointName); +#else if (savepointName.Length == 0) throw new ArgumentException("savepointName must not be empty", nameof(savepointName)); +#endif using var cmd = new MySqlCommand(command + "savepoint " + QuoteIdentifier(savepointName), Connection, this) { NoActivity = true }; await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false); @@ -269,8 +277,12 @@ private async Task DoRollback(IOBehavior ioBehavior, CancellationToken cancellat private void VerifyValid() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(m_isDisposed, this); +#else if (m_isDisposed) throw new ObjectDisposedException(nameof(MySqlTransaction)); +#endif if (Connection is null) throw new InvalidOperationException("Already committed or rolled back."); if (Connection.CurrentTransaction is null) diff --git a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs index c79aedd81..275daa133 100644 --- a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs @@ -22,7 +22,7 @@ public static AuthenticationMethodSwitchRequestPayload Create(ReadOnlySpan // if the packet is just the header byte (0xFE), it's an "Old Authentication Method Switch Request Packet" // (possibly sent by a server that doesn't support CLIENT_PLUGIN_AUTH) name = "mysql_old_password"; - data = Array.Empty(); + data = []; } else { diff --git a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs index 898b0b4f8..6a01a808c 100644 --- a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs @@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads; internal static class EmptyPayload { - public static PayloadData Instance { get; } = new(Array.Empty()); + public static PayloadData Instance { get; } = new([]); } diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs index 72c23b59e..cc8dce3af 100644 --- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs +++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs @@ -37,7 +37,7 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s writer.Write((byte) characterSet); // NOTE: not new byte[19]; see https://github.com/dotnet/roslyn/issues/33088 - ReadOnlySpan padding = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + ReadOnlySpan padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; writer.Write(padding); if ((serverCapabilities & ProtocolCapabilities.LongPassword) == 0) diff --git a/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs b/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs index e9c6f53fc..2beed8ed3 100644 --- a/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs @@ -42,9 +42,11 @@ public static InitialHandshakePayload Create(ReadOnlySpan span) if ((protocolCapabilities & ProtocolCapabilities.SecureConnection) != 0) { var authPluginData2 = reader.ReadByteString(Math.Max(13, authPluginDataLength - 8)); + + // TODO: authPluginData = [..authPluginData1, ..authPluginData2]; when codegen doesn't use an intermediate List authPluginData = new byte[authPluginData1.Length + authPluginData2.Length]; authPluginData1.CopyTo(authPluginData); - authPluginData2.CopyTo(new Span(authPluginData)[authPluginData1.Length..]); + authPluginData2.CopyTo(authPluginData.AsSpan(authPluginData1.Length)); } if ((protocolCapabilities & ProtocolCapabilities.PluginAuth) != 0) authPluginName = Encoding.UTF8.GetString(reader.ReadNullOrEofTerminatedByteString()); diff --git a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs index 9b9eee3d1..d6b02f768 100644 --- a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs @@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads; internal static class PingPayload { - public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.Ping }); + public static PayloadData Instance { get; } = new([ (byte) CommandKind.Ping ]); } diff --git a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs index 72f83b5e0..1d416eddd 100644 --- a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs @@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads; internal static class QuitPayload { - public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.Quit }); + public static PayloadData Instance { get; } = new([ (byte) CommandKind.Quit ]); } diff --git a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs index c2285511e..fe1ab10a2 100644 --- a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs +++ b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs @@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads; internal static class ResetConnectionPayload { - public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.ResetConnection }); + public static PayloadData Instance { get; } = new([ (byte) CommandKind.ResetConnection ]); } diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs index 5c90acfe9..5fca2ba4f 100644 --- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs +++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs @@ -10,7 +10,7 @@ namespace MySqlConnector.Protocol.Serialization; internal static class AuthenticationUtility { public static byte[] CreateAuthenticationResponse(ReadOnlySpan challenge, string password) => - string.IsNullOrEmpty(password) ? Array.Empty() : HashPassword(challenge, password); + string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password); /// /// Hashes a password with the "Secure Password Authentication" method. @@ -54,14 +54,8 @@ public static byte[] HashPassword(ReadOnlySpan challenge, string password) return hashedPassword.ToArray(); } - public static byte[] CreateScrambleResponse(ReadOnlySpan nonce, string password) - { - var scrambleResponse = string.IsNullOrEmpty(password) - ? Array.Empty() - : HashPasswordWithNonce(nonce, password); - - return scrambleResponse; - } + public static byte[] CreateScrambleResponse(ReadOnlySpan nonce, string password) => + string.IsNullOrEmpty(password) ? [] : HashPasswordWithNonce(nonce, password); #if NET5_0_OR_GREATER [SkipLocalsInit] diff --git a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs index fa597c38a..5d15150ec 100644 --- a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs +++ b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs @@ -14,7 +14,17 @@ public ByteArrayReader(ReadOnlySpan buffer) public int Offset { readonly get => m_offset; - set => m_offset = value >= 0 && value <= m_maxOffset ? value : throw new ArgumentOutOfRangeException(nameof(value), $"value must be between 0 and {m_maxOffset:d}"); + set + { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(value); + ArgumentOutOfRangeException.ThrowIfGreaterThan(value, m_maxOffset); +#else + if (value < 0 || value > m_maxOffset) + throw new ArgumentOutOfRangeException(nameof(value), $"value must be between 0 and {m_maxOffset:d}"); +#endif + m_offset = value; + } } public byte ReadByte() @@ -63,8 +73,13 @@ public uint ReadUInt32() public uint ReadFixedLengthUInt32(int length) { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(length, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(length, 4); +#else if (length is <= 0 or > 4) throw new ArgumentOutOfRangeException(nameof(length)); +#endif VerifyRead(length); uint result = 0; for (var i = 0; i < length; i++) @@ -75,8 +90,13 @@ public uint ReadFixedLengthUInt32(int length) public ulong ReadFixedLengthUInt64(int length) { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(length, 0); + ArgumentOutOfRangeException.ThrowIfGreaterThan(length, 8); +#else if (length is <= 0 or > 8) throw new ArgumentOutOfRangeException(nameof(length)); +#endif VerifyRead(length); ulong result = 0; for (var i = 0; i < length; i++) diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs index aabcae82a..6deb3e6a2 100644 --- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs +++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs @@ -144,7 +144,12 @@ public void WriteAscii(ReadOnlySpan chars) { if (m_output.Length < chars.Length) Reallocate(chars.Length); +#if NET8_0_OR_GREATER + Ascii.FromUtf16(chars, m_output.Span, out var bytesWritten); + m_output = m_output[bytesWritten..]; +#else m_output = m_output[Encoding.ASCII.GetBytes(chars, m_output.Span)..]; +#endif } public void WriteLengthEncodedString(StringBuilder stringBuilder) @@ -191,49 +196,37 @@ public void WriteLengthEncodedString(StringBuilder stringBuilder) public void WriteString(short value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(6), out var bytesWritten); m_output = m_output[bytesWritten..]; } public void WriteString(ushort value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(5), out var bytesWritten); m_output = m_output[bytesWritten..]; } public void WriteString(int value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(11), out var bytesWritten); m_output = m_output[bytesWritten..]; } public void WriteString(uint value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(10), out var bytesWritten); m_output = m_output[bytesWritten..]; } public void WriteString(long value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(20), out var bytesWritten); m_output = m_output[bytesWritten..]; } public void WriteString(ulong value) { - int bytesWritten; - while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten)) - Reallocate(); + Utf8Formatter.TryFormat(value, GetSpan(20), out var bytesWritten); m_output = m_output[bytesWritten..]; } @@ -241,7 +234,7 @@ private void Reallocate(int additional = 0) { var usedLength = Position; var newBuffer = ArrayPool.Shared.Rent(Math.Max(usedLength + additional, m_buffer.Length * 2)); - Buffer.BlockCopy(m_buffer, 0, newBuffer, 0, usedLength); + m_buffer.AsSpan(0, usedLength).CopyTo(newBuffer); ArrayPool.Shared.Return(m_buffer); m_buffer = newBuffer; m_output = new(m_buffer, usedLength, m_buffer.Length - usedLength); diff --git a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs index 337418667..6650dc563 100644 --- a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs +++ b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs @@ -26,14 +26,24 @@ public IByteHandler ByteHandler { get { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(m_byteHandler is null, this); +#else if (m_byteHandler is null) throw new ObjectDisposedException(nameof(StandardPayloadHandler)); +#endif return m_byteHandler; } set { var oldByteHandler = m_byteHandler; - m_byteHandler = value ?? throw new ArgumentNullException(nameof(value)); +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(value); +#else + if (value is null) + throw new ArgumentNullException(nameof(value)); +#endif + m_byteHandler = value; oldByteHandler?.Dispose(); m_bufferedByteReader = new(); } diff --git a/src/MySqlConnector/Utilities/ResizableArraySegment.cs b/src/MySqlConnector/Utilities/ResizableArraySegment.cs index d2a0d09b7..1137ddde8 100644 --- a/src/MySqlConnector/Utilities/ResizableArraySegment.cs +++ b/src/MySqlConnector/Utilities/ResizableArraySegment.cs @@ -3,19 +3,12 @@ namespace MySqlConnector.Utilities; /// /// An that supports having its underlying array reallocated and resized. /// -internal readonly struct ResizableArraySegment +internal readonly struct ResizableArraySegment(ResizableArray array, int offset, int count) where T : notnull { - public ResizableArraySegment(ResizableArray array, int offset, int count) - { - Array = array; - Offset = offset; - Count = count; - } - - public ResizableArray Array { get; } - public int Offset { get; } - public int Count { get; } + public ResizableArray Array { get; } = array; + public int Offset { get; } = offset; + public int Count { get; } = count; public static implicit operator ReadOnlySpan(ResizableArraySegment segment) => new ReadOnlySpan(segment.Array.Array, segment.Offset, segment.Count); } diff --git a/src/MySqlConnector/Utilities/TimerQueue.cs b/src/MySqlConnector/Utilities/TimerQueue.cs index 44ee1f8e0..5aa05f736 100644 --- a/src/MySqlConnector/Utilities/TimerQueue.cs +++ b/src/MySqlConnector/Utilities/TimerQueue.cs @@ -14,8 +14,12 @@ internal sealed class TimerQueue /// A timer ID that can be passed to to cancel the timer. public uint Add(int delay, Action action) { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfNegative(delay); +#else if (delay < 0) throw new ArgumentOutOfRangeException(nameof(delay), $"delay must not be negative: {delay}"); +#endif var current = Environment.TickCount; lock (m_lock) @@ -64,7 +68,7 @@ private TimerQueue() { m_lock = new(); m_timer = new(Callback, this, -1, -1); - m_timeoutActions = new(); + m_timeoutActions = []; } private void Callback(object? obj) diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs index c4b9b30d1..651ef5bff 100644 --- a/src/MySqlConnector/Utilities/Utility.cs +++ b/src/MySqlConnector/Utilities/Utility.cs @@ -180,7 +180,7 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan data, bool isPr if (!isPrivate) { // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" - ReadOnlySpan rsaOid = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; + ReadOnlySpan rsaOid = [ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 ]; if (!data.Slice(0, rsaOid.Length).SequenceEqual(rsaOid)) throw new FormatException($"Expected RSA OID but read {BitConverter.ToString(data.Slice(0, 15).ToArray())}"); data = data.Slice(rsaOid.Length); @@ -511,9 +511,17 @@ public static Task WriteAsync(this Stream stream, ReadOnlyMemory data) public static void Write(this Stream stream, ReadOnlyMemory data) => stream.Write(data.Span); #endif +#if !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER + public static bool StartsWith(this string str, char value) => !string.IsNullOrEmpty(str) && str[0] == value; +#endif + public static void SwapBytes(Span bytes, int offset1, int offset2) { +#if NET8_0_OR_GREATER + ref var first = ref Unsafe.AsRef(ref bytes[0]); +#else ref var first = ref Unsafe.AsRef(bytes[0]); +#endif (Unsafe.Add(ref first, offset2), Unsafe.Add(ref first, offset1)) = (Unsafe.Add(ref first, offset1), Unsafe.Add(ref first, offset2)); } diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj index bfba7909c..246efa5b1 100644 --- a/tests/Conformance.Tests/Conformance.Tests.csproj +++ b/tests/Conformance.Tests/Conformance.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 0.1.0 true true @@ -11,10 +11,10 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/IntegrationTests/BatchTests.cs b/tests/IntegrationTests/BatchTests.cs index 0755d67d9..04b5bb17c 100644 --- a/tests/IntegrationTests/BatchTests.cs +++ b/tests/IntegrationTests/BatchTests.cs @@ -8,6 +8,18 @@ public BatchTests(DatabaseFixture database) { } + [Fact] + public void CanCreateParameter() + { + Assert.True(new MySqlBatchCommand().CanCreateParameter); + } + + [Fact] + public void CreateParameter() + { + Assert.IsType(new MySqlBatchCommand().CreateParameter()); + } + [Fact] public void NeedsConnection() { diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj index 04d05b2fb..8c975155b 100644 --- a/tests/IntegrationTests/IntegrationTests.csproj +++ b/tests/IntegrationTests/IntegrationTests.csproj @@ -1,12 +1,12 @@ - net462;net472;net6.0;net7.0 + net462;net472;net6.0;net8.0 false - net7.0 + net8.0 MYSQL_DATA $(NoWarn);MSB3246 @@ -29,17 +29,17 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + + + + @@ -56,7 +56,7 @@ - + diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs index 71c78dd4d..d1f6d5a9d 100644 --- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs +++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs @@ -36,6 +36,9 @@ public void Stop() } m_connections.Clear(); m_tasks.Clear(); +#if NET8_0_OR_GREATER + m_tcpListener.Dispose(); +#endif m_cts.Dispose(); m_cts = null; } diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj index 9fff07634..419961696 100644 --- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj +++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj @@ -1,7 +1,7 @@ - net481;net7.0 + net481;net8.0 @@ -19,16 +19,12 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all + @@ -46,7 +42,7 @@ - + diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs index 77475e4ba..dac24b5bf 100644 --- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs +++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Reflection; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -23,8 +24,12 @@ internal sealed partial class SchemaProvider { public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string collectionName, string?[]? restrictionValues, CancellationToken cancellationToken) { + #if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(collectionName); + #else if (collectionName is null) throw new ArgumentNullException(nameof(collectionName)); + #endif var dataTable = new DataTable(); """); @@ -72,8 +77,8 @@ public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string c codeWriter.WriteLine(""" dataTable.TableName = tableName; - dataTable.Columns.AddRange(new DataColumn[] - { + dataTable.Columns.AddRange( + [ """); foreach (var column in schema.Columns) { @@ -82,7 +87,7 @@ public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string c """); } codeWriter.WriteLine(""" - }); + ]); """); if (schema.Table is string table) @@ -168,7 +173,7 @@ public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string c docWriter.Write($""" --- date: 2021-04-24 - lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd")} + lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)} menu: main: parent: getting started @@ -185,6 +190,7 @@ public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string c """); +#pragma warning disable CA1308 // Normalize strings to uppercase foreach (var schema in schemaCollections) docWriter.Write($@"* `{schema.Name}`{(schema.Description is not null ? "—[" + schema.Description + "](../schema/" + schema.Name.ToLowerInvariant() + "/)" : "")} "); @@ -192,10 +198,11 @@ public async ValueTask GetSchemaAsync(IOBehavior ioBehavior, string c foreach (var schema in schemaCollections.Where(x => x.Description is not null)) { using var schemaDocWriter = new StreamWriter($@"..\..\..\..\..\docs\content\overview\schema\{schema.Name.ToLowerInvariant()}.md"); +#pragma warning restore CA1308 // Normalize strings to uppercase schemaDocWriter.Write($""" --- date: 2022-07-10 - lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd")} + lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)} title: {schema.Name} Schema --- @@ -226,7 +233,8 @@ Restriction Name | Restriction Default | Restriction Number } } -class Schema +#pragma warning disable CA1812 // Avoid uninstantiated internal classes +internal sealed class Schema { public required string Name { get; init; } public string? Description { get; init; } @@ -237,7 +245,7 @@ class Schema public List? Restrictions { get; init; } } -class Column +internal sealed class Column { public required string Name { get; init; } public required string Type { get; init; } @@ -245,7 +253,7 @@ class Column public bool Optional { get; init; } } -class Restriction +internal sealed class Restriction { public required string Name { get; init; } public required string Default { get; init; } diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj index 300f65cc5..73e62aa5e 100644 --- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj +++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj @@ -2,9 +2,10 @@ Exe - net7.0 + net8.0 enable enable + true @@ -12,7 +13,7 @@ - +