diff --git a/BUILDGUIDE.md b/BUILDGUIDE.md index dcb2257745..05b0920307 100644 --- a/BUILDGUIDE.md +++ b/BUILDGUIDE.md @@ -70,7 +70,7 @@ msbuild -t:BuildTestsNetCore ```bash msbuild -t:BuildTestsNetFx -# Build the tests for the .NET Framework (NetFx) driver in 'Debug' Configuration. Default .NET Framework version is 4.6.1. +# Build the tests for the .NET Framework (NetFx) driver in 'Debug' Configuration. Default .NET Framework version is 4.6.2. ``` ```bash @@ -163,6 +163,11 @@ Manual Tests require the below setup to run: |TCPConnectionString | Connection String for a TCP enabled SQL Server instance. | `Server={servername};Database={Database_Name};Trusted_Connection=True;`
OR `Data Source={servername};Initial Catalog={Database_Name};Integrated Security=True;`| |NPConnectionString | Connection String for a Named Pipes enabled SQL Server instance.| `Server=\\{servername}\pipe\sql\query;Database={Database_Name};Trusted_Connection=True;`
OR
`Data Source=np:{servername};Initial Catalog={Database_Name};Integrated Security=True;`| |TCPConnectionStringHGSVBS | (Optional) Connection String for a TCP enabled SQL Server with Host Guardian Service (HGS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = HGS; Enclave Attestation Url = {AttestationURL};`| + |TCPConnectionStringAASVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| + |TCPConnectionStringNoneVBS | (Optional) Connection String for a TCP enabled SQL Server with a VBS Enclave and using None Attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = NONE;`| + |TCPConnectionStringAASSGX | (Optional) Connection String for a TCP enabled SQL Server with a SGX Enclave and using Microsoft Azure Attestation (AAS) attestation protocol configuration. | `Server=tcp:{servername}; Database={Database_Name}; UID={UID}; PWD={PWD}; Attestation Protocol = AAS; Enclave Attestation Url = {AttestationURL};`| + |EnclaveEnabled | Enables tests requiring an enclave-configured server.| + |TracingEnabled | Enables EventSource related tests | |AADAuthorityURL | (Optional) Identifies the OAuth2 authority resource for `Server` specified in `AADPasswordConnectionString` | `https://login.windows.net/`, where `` is the tenant ID of the Azure Active Directory (Azure AD) tenant | |AADPasswordConnectionString | (Optional) Connection String for testing Azure Active Directory Password Authentication. | `Data Source={server.database.windows.net}; Initial Catalog={Azure_DB_Name};Authentication=Active Directory Password; User ID={AAD_User}; Password={AAD_User_Password};`| |AADSecurePrincipalId | (Optional) The Application Id of a registered application which has been granted permission to the database defined in the AADPasswordConnectionString. | {Application ID} | @@ -171,12 +176,14 @@ Manual Tests require the below setup to run: |AzureKeyVaultTenantId | (Optional) The Azure Active Directory tenant (directory) Id of the service principal. | _{Tenant ID of Active Directory}_ | |AzureKeyVaultClientId | (Optional) "Application (client) ID" of an Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL`. Requires the key permissions Get, List, Import, Decrypt, Encrypt, Unwrap, Wrap, Verify, and Sign. | _{Client Application ID}_ | |AzureKeyVaultClientSecret | (Optional) "Client Secret" of the Active Directory registered application, granted access to the Azure Key Vault specified in `AZURE_KEY_VAULT_URL` | _{Client Application Secret}_ | + |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| |LocalDbAppName | (Optional) If Local Db Testing is supported, this property configures the name of Local DB App instance available in client environment. Empty string value disables Local Db testing. | Name of Local Db App to connect to.| |LocalDbSharedInstanceName | (Optional) If LocalDB testing is supported and the instance is shared, this property configures the name of the shared instance of LocalDB to connect to. | Name of shared instance of LocalDB. | - |SupportsIntegratedSecurity | (Optional) Whether or not the USER running tests has integrated security access to the target SQL Server.| `true` OR `false`| |FileStreamDirectory | (Optional) If File Stream is enabled on SQL Server, pass local directory path to be used for setting up File Stream enabled database. | `D:\\escaped\\absolute\\path\\to\\directory\\` | |UseManagedSNIOnWindows | (Optional) Enables testing with Managed SNI on Windows| `true` OR `false`| + |DNSCachingConnString | Connection string for a server that supports DNS Caching| |IsAzureSynpase | (Optional) When set to 'true', test suite runs compatible tests for Azure Synapse/Parallel Data Warehouse. | `true` OR `false`| + |EnclaveAzureDatabaseConnString | (Optional) Connection string for Azure database with enclaves | |MakecertPath | The full path to makecert.exe. This is not required if the path is present in the PATH environment variable. | `D:\\escaped\\absolute\\path\\to\\makecert.exe` | ### Commands to run Manual Tests @@ -274,7 +281,7 @@ Tests can be built and run with custom Target Frameworks. See the below examples ```bash msbuild -t:BuildTestsNetFx -p:TargetNetFxVersion=net462 # Build the tests for custom TargetFramework (.NET Framework) -# Applicable values: net461 (Default) | net462 | net47 | net471 net472 | net48 +# Applicable values: net462 (Default) | net462 | net47 | net471 net472 | net48 ``` ```bash @@ -288,7 +295,7 @@ msbuild -t:BuildTestsNetCore -p:TargetNetCoreVersion=netcoreapp3.1 ```bash dotnet test -p:TargetNetFxVersion=net462 ... # Use above property to run Functional Tests with custom TargetFramework (.NET Framework) -# Applicable values: net461 (Default) | net462 | net47 | net471 net472 | net48 +# Applicable values: net462 (Default) | net462 | net47 | net471 net472 | net48 dotnet test -p:TargetNetCoreVersion=netcoreapp3.1 ... # Use above property to run Functional Tests with custom TargetFramework (.NET Core) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a605c825..8209ff0d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,166 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +## [Stable release 5.0.0] - 2022-08-05 + +This update brings the below changes over the previous release: + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify `Encrypt=Strict` in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Added `TDS 8` version for TDSLogin. [#1657](https://github.com/dotnet/SqlClient/pull/1657) + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1688](https://github.com/dotnet/SqlClient/pull/1688) +- Fixed **KeyNotFoundException** for the `FailoverPartner` key on SQL servers with availability group configured. [#1614](https://github.com/dotnet/SqlClient/pull/1614) +- Fixed small inconsistency between netcore and netfx for `EncryptionOptions`. [#1672](https://github.com/dotnet/SqlClient/pull/1672) +- Fixed `Microsoft.SqlServer.Server` netcore project package reference. [#1654](https://github.com/dotnet/SqlClient/pull/1654) + +### Changed + +- Updated `AuthProviderInfo` struct to be matched the changes in native SNI for `TDS 8` server certificate validation. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated default system protocol for `TDS 8` on managed code. [#1678](https://github.com/dotnet/SqlClient/pull/1678) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0`. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated **IdentityModel** dependency from 6.8.0 to 6.21.0 and **IdentityClient** from 4.32.2 to 4.45.0. [#1646](https://github.com/dotnet/SqlClient/pull/1646) +- Changed from union overlay design to reflected interfaces for SqlTypes. [1647](https://github.com/dotnet/SqlClient/pull/1647) + +## [Preview Release 5.0.0-preview3.22168.1] - 2022-06-16 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview2 + +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) + +### Fixed + +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637](https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625](https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500](https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed NullReferenceException when using `SqlDependency.Start` against an Azure SQL Database.[#1294](https://github.com/dotnet/SqlClient/pull/1294) +- Send the correct retained transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624](https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests (instance name resolution) on Linux and macOS when MultiSubNetFailover is specified. [#1578](https://github.com/dotnet/SqlClient/pull/1578) +- Adjust the default ConnectRetryCount against Azure Synapse OnDemand endpoints [#1626](https://github.com/dotnet/SqlClient/pull/1626) + +### Changed + +- Code health improvements [#1353](https://github.com/dotnet/SqlClient/pull/1353) [#1354](https://github.com/dotnet/SqlClient/pull/1354) [#1525](https://github.com/dotnet/SqlClient/pull/1525) [#1186](https://github.com/dotnet/SqlClient/pull/1186) +- Update Azure Identity dependency from 1.5.0 to 1.6.0.[#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for SqlCommandSet [#1548](https://github.com/dotnet/SqlClient/pull/1548) +- Rework on `TdsParserStateObjectManaged` with nullable annotations. [#1555](https://github.com/dotnet/SqlClient/pull/1555) + +## [Preview Release 5.0.0-preview2.22096.2] - 2022-04-06 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview1 + +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + +### Fixed + +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication [#1559](https://github.com/dotnet/SqlClient/pull/1559) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0-preview2.22084.1`. [#1563](https://github.com/dotnet/SqlClient/pull/1563) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1` [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced AlwaysEncryptedAttestationException with SqlException [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to SqlParameterCollection [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Code health improvements [#1343](https://github.com/dotnet/SqlClient/pull/1343) [#1370](https://github.com/dotnet/SqlClient/pull/1370) [#1371](https://github.com/dotnet/SqlClient/pull/1371) [#1438](https://github.com/dotnet/SqlClient/pull/1438) [#1483](https://github.com/dotnet/SqlClient/pull/1483) + +## [Preview Release 5.0.0-preview1.22069.1] - 2022-03-09 + +### Added + +- Added SqlDataSourceEnumerator. [#1430](https://github.com/dotnet/SqlClient/pull/1430) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457) + +### Fixed + +- Fixed all documentation paths to Unix format path. [#1442](https://github.com/dotnet/SqlClient/pull/1442) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v5.0.0-preview1.22062.1`. [#1537](https://github.com/dotnet/SqlClient/pull/1537) +- Modernized style in ValueUtilSmi. [#1351](https://github.com/dotnet/SqlClient/pull/1351) +- Changed SQL server codenames to version names. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Prevented subtype generation in project files. [#1452](https://github.com/dotnet/SqlClient/pull/1452) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Changed files in csproj to be alphabetically sorted in netfx and netcore. [#1364](https://github.com/dotnet/SqlClient/pull/1364) +- Sqlstream, SqlInternalTransaction and MetaDataUtilsSmi are moved to shared folder. [#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346) and [#1339](https://github.com/dotnet/SqlClient/pull/1339) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313),[#1330](https://github.com/dotnet/SqlClient/pull/1330),[#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435),[#1478](https://github.com/dotnet/SqlClient/pull/1478) + +## [Stable release 4.1.1] - 2022-09-13 + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1706](https://github.com/dotnet/SqlClient/pull/1706) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1708](https://github.com/dotnet/SqlClient/pull/1708), [#1746](https://github.com/dotnet/SqlClient/pull/1746) +- Added CommandText length validation when using stored procedure command types. [#1709](https://github.com/dotnet/SqlClient/pull/1709) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1710](https://github.com/dotnet/SqlClient/pull/1710) +- Fixed null SqlBinary as rowversion. [#1712](https://github.com/dotnet/SqlClient/pull/1712) +- Fixed table's collation overriding with default UTF8 collation. [#1749](https://github.com/dotnet/SqlClient/pull/1749) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1755](https://github.com/dotnet/SqlClient/pull/1755), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1711](https://github.com/dotnet/SqlClient/pull/1711) + +## [Stable release 4.1.0] - 2022-01-31 + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1419](https://github.com/dotnet/SqlClient/pull/1419) [#1425](https://github.com/dotnet/SqlClient/pull/1425) + +## [Stable release 4.0.2] - 2022-09-13 + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1718](https://github.com/dotnet/SqlClient/pull/1718) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1720](https://github.com/dotnet/SqlClient/pull/1720), [#1747](https://github.com/dotnet/SqlClient/pull/1747) +- Added CommandText length validation when using stored procedure command types. [#1721](https://github.com/dotnet/SqlClient/pull/1721) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1722](https://github.com/dotnet/SqlClient/pull/1722) +- Fixed null SqlBinary as rowversion. [#1724](https://github.com/dotnet/SqlClient/pull/1724) +- Fixed table's collation overriding with default UTF8 collation. [#1750](https://github.com/dotnet/SqlClient/pull/1750) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1754](https://github.com/dotnet/SqlClient/pull/1754), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1723](https://github.com/dotnet/SqlClient/pull/1723) + +## [Stable release 4.0.1] - 2022-01-17 + +### Added + +Added AppContext switch `SuppressInsecureTLSWarning` to allow suppression of TLS security warning when using `Encrypt=false` in the connection string. [#1457](https://github.com/dotnet/SqlClient/pull/1457) + +### Fixed + +- Fixed Kerberos authentication failure when using .NET 6. [#1411](https://github.com/dotnet/SqlClient/pull/1411) +- Fixed connection failure when using `SqlLocalDB` instance pipe name. [#1433](https://github.com/dotnet/SqlClient/pull/1433) +- Fixed a failure when executing concurrent queries requiring enclaves. [#1451](https://github.com/dotnet/SqlClient/pull/1451) +- Updated obsolete API calls targeting .NET 6. [#1401](https://github.com/dotnet/SqlClient/pull/1401) + ## [Stable Release 4.0.0] - 2021-11-18 ### Added @@ -104,6 +264,26 @@ This update brings the below changes over the previous release: - Optimized async method allocations in .NET Framework by porting changes from .NET Core. [#1084](https://github.com/dotnet/SqlClient/pull/1084) - Various code improvements [#902](https://github.com/dotnet/SqlClient/pull/902) [#925](https://github.com/dotnet/SqlClient/pull/925) [#933](https://github.com/dotnet/SqlClient/pull/933) [#934](https://github.com/dotnet/SqlClient/pull/934) [#1024](https://github.com/dotnet/SqlClient/pull/1024) [#1057](https://github.com/dotnet/SqlClient/pull/1057) [#1122](https://github.com/dotnet/SqlClient/pull/1122) [#1133]((https://github.com/dotnet/SqlClient/pull/1133)) [#1134](https://github.com/dotnet/SqlClient/pull/1134) [#1141](https://github.com/dotnet/SqlClient/pull/1141) [#1187](https://github.com/dotnet/SqlClient/pull/1187) [#1188](https://github.com/dotnet/SqlClient/pull/1188) [#1223](https://github.com/dotnet/SqlClient/pull/1223) [#1225](https://github.com/dotnet/SqlClient/pull/1225) [#1226](https://github.com/dotnet/SqlClient/pull/1226) +## [Stable release 3.1.1] - 2022-08-12 + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1700](https://github.com/dotnet/SqlClient/pull/1700) +- Fixed Kerberos authentication failure when using .NET 6. [#1696](https://github.com/dotnet/SqlClient/pull/1696) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1695](https://github.com/dotnet/SqlClient/pull/1695) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1699](https://github.com/dotnet/SqlClient/pull/1699) + +## [Stable release 3.1.0] - 2022-03-30 + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1539](https://github.com/dotnet/SqlClient/pull/1539) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1560](https://github.com/dotnet/SqlClient/pull/1560) + +### Fixed + +- Changed EnclaveDelegate.Crypto GetEnclaveProvider to use a thread safe concurrent dictionary. [#1564](https://github.com/dotnet/SqlClient/pull/1564 + ## [Stable Release 3.0.1] - 2021-09-24 ### Fixed @@ -130,6 +310,14 @@ This update brings the below changes over the previous release: ### Breaking Changes - Modified column encryption key store provider registrations to give built-in system providers precedence over providers registered on connection and command instances. [#1101](https://github.com/dotnet/SqlClient/pull/1101) +## [Stable Release 2.1.5] - 2022-08-30 + +### Fixed + +- Added CommandText length validation when using stored procedure command types. [#1726](https://github.com/dotnet/SqlClient/pull/1726) +- Fixed Kerberos authentication failure when using .NET 6. [#1727](https://github.com/dotnet/SqlClient/pull/1727) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1729](https://github.com/dotnet/SqlClient/pull/1729) + ## [Stable Release 2.1.4] - 2021-09-20 ### Fixed diff --git a/README.md b/README.md index e979397efa..fe57a1ce45 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Microsoft.Data.SqlClient is a data provider for Microsoft SQL Server and Azure S The Microsoft.Data.SqlClient package supports the below environments: -- .NET Framework 4.6.1+ +- .NET Framework 4.6.2+ - .NET Core 3.1+ - .NET Standard 2.0+ diff --git a/RunTests.cmd b/RunTests.cmd index c905d1150f..663c3da9b4 100644 --- a/RunTests.cmd +++ b/RunTests.cmd @@ -97,7 +97,7 @@ call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetCore -p:Re call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net5.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore5.0-functional-anycpu.xml call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Configuration="Release" -p:TestTargetOS="Windowsnetcoreapp" --no-build -v n --filter "category!=nonnetcoreapptests&category!=failing&category!=nonwindowstests" -p:Platform="AnyCPU" -p:TargetNetCoreVersion=net5.0 -p:ReferenceType=NetStandard -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\netstandard-netcore5.0-manual-anycpu.xml -:: TESTING 'NETSTANDARD' REFERENCE TYPE WITH .NET FRAMEWORK 4.6.1+ AS TARGET FRAMEWORK IS INVALID CASE AS PROJECT REFERENCE DOES NOT LOAD SNI.DLL IN .NET FRAMEWORK RUNTIME. +:: TESTING 'NETSTANDARD' REFERENCE TYPE WITH .NET FRAMEWORK 4.6.2+ AS TARGET FRAMEWORK IS INVALID CASE AS PROJECT REFERENCE DOES NOT LOAD SNI.DLL IN .NET FRAMEWORK RUNTIME. :: CASE IS VERIFIED WITH RUNTIME.NATIVE.SYSTEM.DATA.SQLCLIENT.SNI AS WELL. TO TEST .NET FRAMEWORK TARGETS, USE 'NETSTANDARDPACKAGE' REFERENCE TYPE ONLY. :: REFERENCE TYPE "PROJECT" (We only build and test AnyCPU with Project Reference) @@ -122,24 +122,24 @@ call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetCoreAllOS call :pauseOnError msbuild -p:Configuration="Release" -t:BuildAKVNetStAllOS call :pauseOnError msbuild -p:Configuration="Release" -t:GenerateAKVProviderNugetPackage call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-manual-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-manual-anycpu.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net48 call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-anycpu.xml call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-anycpu.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=x64 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-manual-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-manual-x64.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=x64 -p:TargetNetFxVersion=net48 call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-x64.xml call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-manual-x64.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=Win32 -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net461-manual-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net462-manual-win32.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=Win32 -p:TargetNetFxVersion=net48 call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\project-net48-functional-Win32.xml @@ -147,24 +147,24 @@ call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\M :: .NET FRAMEWORK REFERENCE TYPE "PACKAGE" call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-functional-anycpu.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-manual-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-anycpu.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-anycpu.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:TargetNetFxVersion=net48 -p:ReferenceType=Package call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-anycpu.xml call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="AnyCPU" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-anycpu.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=x64 -p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-functional-x64.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-manual-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-x64.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-x64.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=x64 -p:TargetNetFxVersion=net48 -p:ReferenceType=Package call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-x64.xml call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="x64" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-manual-x64.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=Win32 -p:ReferenceType=Package -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-functional-win32.xml -call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net461-manual-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-functional-win32.xml +call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\ManualTests\Microsoft.Data.SqlClient.ManualTesting.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net462-manual-win32.xml call :pauseOnError msbuild -p:Configuration="Release" -t:BuildTestsNetFx -p:Platform=Win32 -p:TargetNetFxVersion=net48 -p:ReferenceType=Package call :pauseOnError dotnet test "src\Microsoft.Data.SqlClient\tests\FunctionalTests\Microsoft.Data.SqlClient.Tests.csproj" -p:Platform="Win32" -p:Configuration="Release" -p:TestTargetOS="Windowsnetfx" -p:TargetNetFxVersion=net48 --no-build -v n --filter "category!=nonnetfxtests&category!=failing&category!=nonwindowstests" -p:ReferenceType=Package -l:trx;LogFileName=..\..\..\..\..\artifacts\Results\package-net48-functional-Win32.xml diff --git a/build.proj b/build.proj index 2aa4ee019b..14efda3f4a 100644 --- a/build.proj +++ b/build.proj @@ -24,6 +24,7 @@ $(TF) $(TF) true + Configuration=$(Configuration);AssemblyVersion=$(SqlServerAssemblyVersion);AssemblyFileVersion=$(SqlServerAssemblyFileVersion);Version=$(SqlServerPackageVersion); Configuration=$(Configuration);AssemblyFileVersion=$(AssemblyFileVersion);TargetsWindows=$(TargetsWindows);TargetsUnix=$(TargetsUnix); BuildProjectReferences=false;$(ProjectProperties);BuildForRelease=false;TargetNetCoreVersion=$(TargetNetCoreVersion);TargetNetFxVersion=$(TargetNetFxVersion) TestResults @@ -37,6 +38,7 @@ + @@ -65,13 +67,18 @@ - - - - - + + + + + + + + + + @@ -101,6 +108,16 @@ + + + + + + + + + + diff --git a/doc/samples/IBinarySerialize.cs b/doc/samples/IBinarySerialize.cs index 749ee5f3f6..64f314f86d 100644 --- a/doc/samples/IBinarySerialize.cs +++ b/doc/samples/IBinarySerialize.cs @@ -2,7 +2,7 @@ using System.IO; using System.Data; using System.Data.SqlTypes; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; using System.Text; namespace test @@ -45,7 +45,7 @@ static void Main(string[] args) // Bytes 0 - 19: string text, padded to the right with null characters // Bytes 20+: Double value - // using Microsoft.Data.SqlClient.Server; + // using Microsoft.SqlServer.Server; public void Read(System.IO.BinaryReader r) { @@ -84,7 +84,7 @@ public void Read(System.IO.BinaryReader r) // Bytes 0 - 19: string text, padded to the right with null characters // Bytes 20+: Double value - // using Microsoft.Data.SqlClient.Server; + // using Microsoft.SqlServer.Server; public void Write(System.IO.BinaryWriter w) { int maxStringSize = 20; diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs new file mode 100644 index 0000000000..3631912ba6 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using Microsoft.SqlServer.Server; + +namespace test +{ + public class Class1 : IBinarySerialize + { + [STAThread] + static void Main(string[] args) + { + string fileName = "info.dat"; + Class1 temp = new Class1(); + + FileStream fs = new FileStream(fileName, FileMode.Create); + BinaryWriter w = new BinaryWriter(fs); + + temp.Write(w); + + w.Close(); + fs.Close(); + + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + BinaryReader r = new BinaryReader(fs); + + temp.Read(r); + + Console.WriteLine("String value: " + temp.StringValue); + Console.WriteLine("Double value: " + temp.DoubleValue); + + r.Close(); + fs.Close(); + } + + public string StringValue; + public double DoubleValue; + + // + // The binary layout is as follows: + // Bytes 0 - 19: string text, padded to the right with null characters + // Bytes 20+: Double value + + // using Microsoft.SqlServer.Server; + public void Read(System.IO.BinaryReader r) + { + + int maxStringSize = 20; + char[] chars; + int stringEnd; + string stringValue; + double doubleValue; + + // Read the characters from the binary stream. + chars = r.ReadChars(maxStringSize); + + // Find the start of the null character padding. + stringEnd = Array.IndexOf(chars, '\0'); + + if (stringEnd == 0) + { + stringValue = null; + return; + } + + // Build the string from the array of characters. + stringValue = new String(chars, 0, stringEnd); + + // Read the double value from the binary stream. + doubleValue = r.ReadDouble(); + + // Set the object's properties equal to the values. + this.StringValue = stringValue; + this.DoubleValue = doubleValue; + } + // + + // + // The binary layout is as follows: + // Bytes 0 - 19: string text, padded to the right with null characters + // Bytes 20+: Double value + + // using Microsoft.SqlServer.Server; + public void Write(System.IO.BinaryWriter w) + { + int maxStringSize = 20; + string stringValue = "The value of PI: "; + string paddedString; + double value = 3.14159; + + // Pad the string from the right with null characters. + paddedString = stringValue.PadRight(maxStringSize, '\0'); + + // Write the string value one byte at a time. + for (int i = 0; i < paddedString.Length; i++) + { + w.Write(paddedString[i]); + } + + // Write the double value. + w.Write(value); + } + // + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs new file mode 100644 index 0000000000..e35b9cad21 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs @@ -0,0 +1,23 @@ +using System.IO; +using System.Collections; +using Microsoft.SqlServer.Server; + +public class Class1 +{ + +// +[SqlFunctionAttribute(FillRowMethodName = "FillFileRow")] +public static IEnumerable GetFileDetails(string directoryPath) +{ + try + { + DirectoryInfo di = new DirectoryInfo(directoryPath); + return di.GetFiles(); + } + catch (DirectoryNotFoundException dnf) + { + return new string[1] { dnf.ToString() }; + } +} +// +} \ No newline at end of file diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs new file mode 100644 index 0000000000..34999b5dd5 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs @@ -0,0 +1,23 @@ +// +using System; +using System.IO; +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedAggregate(Microsoft.SqlServer.Server.Format.UserDefined, + IsInvariantToNulls = true, + IsInvariantToDuplicates = false, + IsInvariantToOrder = false, + MaxByteSize = 8000) + ] +public class Concatenate : Microsoft.SqlServer.Server.IBinarySerialize +{ + public void Read(BinaryReader r) + { + } + + public void Write(BinaryWriter w) + { + } +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs new file mode 100644 index 0000000000..90bfccfce4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs @@ -0,0 +1,120 @@ +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; +using System.Text; + +// +[Serializable] +[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native, + IsByteOrdered=true, + Name="Point",ValidationMethodName = "ValidatePoint")] +public struct Point : INullable +{ +// + private bool is_Null; + private int _x; + private int _y; + + public bool IsNull + { + get + { + return (is_Null); + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return pt; + } + } + + // Use StringBuilder to provide string representation of UDT. + public override string ToString() + { + // Since InvokeIfReceiverIsNull defaults to 'true' + // this test is unnecessary if Point is only being called + // from SQL. + if (this.IsNull) + { + return "NULL"; + } + else + { + StringBuilder builder = new StringBuilder(); + builder.Append(_x); + builder.Append(","); + builder.Append(_y); + return builder.ToString(); + } + } + + [SqlMethod(OnNullCall = false)] + public static Point Parse(SqlString s) + { + // With OnNullCall=false, this check is unnecessary if + // Point only called from SQL. + if (s.IsNull) + return Null; + + // Parse input string to separate out points. + Point pt = new Point(); + string[] xy = s.Value.Split(",".ToCharArray()); + pt.X = int.Parse(xy[0]); + pt.Y = int.Parse(xy[1]); + + // Call ValidatePoint to enforce validation + // for string conversions. + if (!pt.ValidatePoint()) + throw new ArgumentException("Invalid XY coordinate values."); + return pt; + } + + // X and Y coordinates exposed as properties. + public int X + { + get + { + return this._x; + } + // Call ValidatePoint to ensure valid range of Point values. + set + { + int temp = _x; + _x = value; + if (!ValidatePoint()) + { + _x = temp; + throw new ArgumentException("Invalid X coordinate value."); + } + } + } + + public int Y + { + get + { + return this._y; + } + set + { + int temp = _y; + _y = value; + if (!ValidatePoint()) + { + _y = temp; + throw new ArgumentException("Invalid Y coordinate value."); + } + } + } + + // Validation method to enforce valid X and Y values. + private bool ValidatePoint() + { + return true; + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs new file mode 100644 index 0000000000..7317b1917b --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs @@ -0,0 +1,45 @@ +using System.Collections; +//----------------------------------------------------------------------------- +// +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +public partial class UserDefinedFunctions +{ + public const double SALES_TAX = .086; + + [SqlFunction()] + public static SqlDouble addTax(SqlDouble originalAmount) + { + SqlDouble taxAmount = originalAmount * SALES_TAX; + + return originalAmount + taxAmount; + } +} +// + +//----------------------------------------------------------------------------- +// +public partial class UserDefinedFunctions +{ + [SqlFunction(Name="sp_scalarFunc")] + public static SqlString SampleScalarFunction(SqlString s) + { + //... + return ""; + } +} +// + +//----------------------------------------------------------------------------- +// +public partial class UserDefinedFunctions +{ + [SqlFunction(Name="sp_tableFunc", TableDefinition="letter nchar(1)")] + public static IEnumerable SampleTableFunction(SqlString s) + { + //... + return new ArrayList(new char[3] {'a', 'b', 'c'}); + } +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs new file mode 100644 index 0000000000..9e41150c99 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs @@ -0,0 +1,126 @@ +using System; +// +using Microsoft.SqlServer.Server; +using System.Data.SqlTypes; +using System.Text; + +[Serializable] +[SqlUserDefinedType(Format.Native, + IsByteOrdered = true, + Name = "Point", ValidationMethodName = "ValidatePoint")] +public struct Point : INullable +{ + + private bool is_Null; + private int _x; + private int _y; + + // Distance from Point to the specified x and y values method. + [SqlMethod(OnNullCall = false, IsMutator = false, InvokeIfReceiverIsNull = false)] + public Double DistanceFromXY(int iX, int iY) + { + return Math.Sqrt(Math.Pow(iX - _x, 2.0) + Math.Pow(iY - _y, 2.0)); + } + // + + public bool IsNull + { + get + { + return (is_Null); + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return pt; + } + } + + // Use StringBuilder to provide string representation of UDT. + public override string ToString() + { + // Since InvokeIfReceiverIsNull defaults to 'true' + // this test is unnecessary if Point is only being called + // from SQL. + if (this.IsNull) + return "NULL"; + else + { + StringBuilder builder = new StringBuilder(); + builder.Append(_x); + builder.Append(","); + builder.Append(_y); + return builder.ToString(); + } + } + + [SqlMethod(OnNullCall = false)] + public static Point Parse(SqlString s) + { + // With OnNullCall=false, this check is unnecessary if + // Point only called from SQL. + if (s.IsNull) + return Null; + + // Parse input string to separate out points. + Point pt = new Point(); + string[] xy = s.Value.Split(",".ToCharArray()); + pt.X = int.Parse(xy[0]); + pt.Y = int.Parse(xy[1]); + + // Call ValidatePoint to enforce validation + // for string conversions. + if (!pt.ValidatePoint()) + throw new ArgumentException("Invalid XY coordinate values."); + return pt; + } + + // X and Y coordinates exposed as properties. + public int X + { + get + { + return this._x; + } + // Call ValidatePoint to ensure valid range of Point values. + set + { + int temp = _x; + _x = value; + if (!ValidatePoint()) + { + _x = temp; + throw new ArgumentException("Invalid X coordinate value."); + } + } + } + + public int Y + { + get + { + return this._y; + } + set + { + int temp = _y; + _y = value; + if (!ValidatePoint()) + { + _y = temp; + throw new ArgumentException("Invalid Y coordinate value."); + } + } + } + + // Validation method to enforce valid X and Y values. + private bool ValidatePoint() + { + return true; + } +} diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs new file mode 100644 index 0000000000..edf119d940 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedAggregateAttribute_Aggregate1.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +[Serializable] +[SqlUserDefinedAggregate(Format.Native)] +public struct CountVowels +{ + // count only the vowels in the passed-in strings + private SqlInt32 countOfVowels; + + public void Init() + { + countOfVowels = 0; + } + + public void Accumulate(SqlString value) + { + // list of vowels to look for + string vowels = "aeiou"; + + // for each character in the given parameter + for (int i=0; i < value.ToString().Length; i++) + { + // for each character in the vowels string + for (int j=0; j < vowels.Length; j++) + { + // convert parameter character to lowercase and compare to vowel + if (value.Value.Substring(i,1).ToLower() == vowels.Substring(j,1)) + { + // it is a vowel, increment the count + countOfVowels+=1; + } + } + } + } + + public void Merge(CountVowels value) + { + Accumulate(value.Terminate()); + } + + public SqlString Terminate() + { + return countOfVowels.ToString(); + } +} +// + +//----------------------------------------------------------------------------- +// +[SqlUserDefinedAggregate(Format.Native)] +public class SampleAggregate +{ + //... +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs new file mode 100644 index 0000000000..70f8d0ae7b --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/csharp/SqlUserDefinedTypeAttribute_Type1.cs @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// +using System; +using System.Data.SqlTypes; +using Microsoft.SqlServer.Server; + +[Serializable()] +[SqlUserDefinedType(Format.Native)] +public struct Point : INullable +{ + private int m_x; + private int m_y; + private bool is_Null; + + public int X + { + get + { + return (this.m_x); + } + set + { + m_x = value; + } + } + + public int Y + { + get + { + return (this.m_y); + } + set + { + m_y = value; + } + } + + public bool IsNull + { + get + { + return is_Null; + } + } + + public static Point Null + { + get + { + Point pt = new Point(); + pt.is_Null = true; + return (pt); + } + } + + public override string ToString() + { + if (this.IsNull) + { + return "NULL"; + } + else + { + return this.m_x + ":" + this.m_y; + } + } + + public static Point Parse(SqlString s) + { + if (s.IsNull) + { + return Null; + } + + // Parse input string here to separate out coordinates + string str = Convert.ToString(s); + string[] xy = str.Split(':'); + + Point pt = new Point(); + pt.X = Convert.Toint(xy[0]); + pt.Y = Convert.Toint(xy[1]); + return (pt); + } + + public SqlString Quadrant() + { + if (m_x == 0 && m_y == 0) + { + return "centered"; + } + + SqlString stringReturn = ""; + + if (m_x == 0) + { + stringReturn = "center"; + } + else if (m_x > 0) + { + stringReturn = "right"; + } + else if (m_x < 0) + { + stringReturn = "left"; + } + + if (m_y == 0) + { + stringReturn = stringReturn + " center"; + } + else if (m_y > 0) + { + stringReturn = stringReturn + " top"; + } + else if (m_y < 0) + { + stringReturn = stringReturn + " bottom"; + } + + return stringReturn; + } +} +// + +//----------------------------------------------------------------------------- +// +[SqlUserDefinedType(Format.Native, MaxByteSize=8000)] +public class SampleType +{ + //... +} +// diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb new file mode 100644 index 0000000000..8f96b056a4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb @@ -0,0 +1,103 @@ +Option Explicit On +Option Strict On + +Imports System +Imports System.IO +Imports Microsoft.SqlServer.Server +Imports System.Text + +Public Class Class1:Implements Microsoft.SqlServer.Server.IBinarySerialize + +Dim StringValue As String +Dim DoubleValue As Double + +Shared Sub Main() + + Dim fileName As String = "info.dat" + Dim temp As New Class1() + + Dim fs As New FileStream(fileName, FileMode.Create) + Dim w As New BinaryWriter(fs) + + temp.Write(w) + + w.Close() + fs.Close() + + fs = New FileStream(fileName, FileMode.Open, FileAccess.Read) + Dim r As New BinaryReader(fs) + + temp.Read(r) + + Console.WriteLine("String Value: " & temp.StringValue) + Console.WriteLine("Double value: " & temp.DoubleValue) + +End Sub + +' +' The binary layout is as follows: +' Bytes 0 - 19: string text, padded to the right with null +' characters +' Bytes 20+: double value +Public Sub Read(ByVal r As System.IO.BinaryReader) _ + Implements Microsoft.SqlServer.Server.IBinarySerialize.Read + + Dim maxStringSize As Integer = 20 + Dim chars As Char() + Dim stringEnd As Integer + Dim stringValue As String + Dim value As double + + ' Read the characters from the binary stream. + chars = r.ReadChars(maxStringSize) + + ' Find the start of the null character padding. + stringEnd = Array.IndexOf(chars, ControlChars.NullChar) + + If StringEnd = 0 Then + stringValue = Nothing + Exit Sub + End If + + ' Build the string from the array of characters. + stringValue = new String(chars, 0, stringEnd) + + ' Read the double value from the binary stream. + value = r.ReadDouble() + + ' Set the object's properties equal to the values. + Me.StringValue = stringValue + Me.DoubleValue = value + +End Sub +' + +' +' The binary layout is as follows: +' Bytes 0 - 19: string text, padded to the right with null characters +' Bytes 20+: Double value +Public Sub Write(ByVal w As System.IO.BinaryWriter) _ + Implements Microsoft.SqlServer.Server.IBinarySerialize.Write + + Dim maxStringSize As Integer = 20 + Dim stringValue As String = "The value of PI: " + Dim paddedString As String + Dim value As Double = 3.14159 + + ' Pad the string from the right with null characters. + paddedString = stringValue.PadRight(maxStringSize, ControlChars.NullChar) + + + ' Write the string value one byte at a time. + Dim i As Integer + For i = 0 To paddedString.Length - 1 + w.Write(paddedString(i)) + Next + + ' Write the double value. + w.Write(value) + +End Sub +' + +End Class diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb new file mode 100644 index 0000000000..41c7bcffcc --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb @@ -0,0 +1,31 @@ +Option Explicit On +Option Strict On + +Imports System.IO +Imports System.Collections +Imports Microsoft.SqlServer.Server + +Public Class Class1 + +' + _ +Public Shared Function GetFileDetails(ByVal directoryPath As String) As IEnumerable + + Try + + Dim di As DirectoryInfo = new DirectoryInfo(directoryPath) + return di.GetFiles() + + Catch dnf As DirectoryNotFoundException + + Dim message As String() = {dnf.ToString() } + return message + + End Try +End Function +' + + + + +End Class \ No newline at end of file diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb new file mode 100644 index 0000000000..2bb1e5af98 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb @@ -0,0 +1,21 @@ +' +Imports System.IO +Imports Microsoft.SqlServer.Server + + _ +Public Class Concatenate + Implements Microsoft.SqlServer.Server.IBinarySerialize + +Public Sub Read(ByVal r As BinaryReader) Implements Microsoft.SqlServer.Server.IBinarySerialize.Read + + End Sub + + Public Sub Write(ByVal w As BinaryWriter) Implements Microsoft.SqlServer.Server.IBinarySerialize.Write + + End Sub +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb new file mode 100644 index 0000000000..e5b2368aa9 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb @@ -0,0 +1,114 @@ +Option Explicit On +Option Strict On + +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server +Imports System.Text + +' + _ + Public Structure Point + Implements INullable +' + Private is_Null As Boolean + Private _x As Integer + Private _y As Integer + + Public ReadOnly Property IsNull() As Boolean _ + Implements INullable.IsNull + Get + Return (is_Null) + End Get + End Property + + Public Shared ReadOnly Property Null() As Point + Get + Dim pt As New Point + pt.is_Null = True + Return (pt) + End Get + End Property + + ' Use StringBuilder to provide string representation of UDT. + Public Overrides Function ToString() As String + ' Since InvokeIfReceiverIsNull defaults to 'true' + ' this test is unneccesary if Point is only being called + ' from SQL. + If Me.IsNull Then + Return "NULL" + Else + Dim builder As StringBuilder = New StringBuilder + builder.Append(_x) + builder.Append(",") + builder.Append(_y) + Return builder.ToString + End If + End Function + + _ + Public Shared Function Parse(ByVal s As SqlString) As Point + ' With OnNullCall=False, this check is unnecessary if + ' Point only being called from SQL. + If s.IsNull Then + Return Null + End If + + ' Parse input string here to separate out points. + Dim pt As New Point() + Dim xy() As String = s.Value.Split(",".ToCharArray()) + pt.X = Integer.Parse(xy(0)) + pt.Y = Integer.Parse(xy(1)) + + ' Call ValidatePoint to enforce validation + ' for string conversions. + If Not pt.ValidatePoint() Then + Throw New ArgumentException("Invalid XY coordinate values.") + End If + Return pt + End Function + + ' X and Y coordinates are exposed as properties. + Public Property X() As Integer + Get + Return (Me._x) + End Get + + Set(ByVal Value As Integer) + Dim temp As Integer = _x + _x = Value + If Not ValidatePoint() Then + _x = temp + Throw New ArgumentException("Invalid X coordinate value.") + End If + End Set + End Property + + Public Property Y() As Integer + Get + Return (Me._y) + End Get + + Set(ByVal Value As Integer) + Dim temp As Integer = _y + _y = Value + If Not ValidatePoint() Then + _y = temp + Throw New ArgumentException("Invalid Y coordinate value.") + End If + End Set + End Property + + ' Validation method to enforce valid X and Y values. + Private Function ValidatePoint() As Boolean + ' Allow only zero or positive integers for X and Y coordinates. + If (_x >= 0) And (_y >= 0) Then + Return True + Else + Return False + End If + End Function + +End Structure diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb new file mode 100644 index 0000000000..b308062cd4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb @@ -0,0 +1,47 @@ +Imports System.Collections +'------------------------------------------------------------------------------ +' +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + +Partial Public Class UserDefinedFunctions + + Public Const SALES_TAX As Double = 0.086 + + + Public Shared Function addTax(ByVal originalAmount As SqlDouble) As SqlDouble + + Dim taxAmount As SqlDouble = originalAmount * SALES_TAX + + Return originalAmount + taxAmount + End Function +End Class +' + + +'------------------------------------------------------------------------------ +' +Partial Public Class UserDefinedFunctions + + + Public Shared Function SampleScalarFunction(ByVal s As SqlString) As SqlString + + '... + Return "" + End Function +End Class +' + + +'------------------------------------------------------------------------------ +' +Partial Public Class UserDefinedFunctions + + + Public Shared Function SampleTableFunction(ByVal s As SqlString) As IEnumerable + + '... + Return New Char(2) {"a"c, "b"c, "c"c} + End Function +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb new file mode 100644 index 0000000000..b2e7727c51 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedAggregateAttribute_Aggregate1.vb @@ -0,0 +1,57 @@ +'------------------------------------------------------------------------------ +' +Imports System +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + + + +Public Structure CountVowels + + ' count only the vowels in the passed-in strings + Private countOfVowels As SqlInt32 + + + Public Sub Init() + countOfVowels = 0 + End Sub + + + Public Sub Accumulate(ByVal value As SqlString) + Dim stringChar As String + Dim indexChar As Integer + + ' for each character in the given parameter + For indexChar = 0 To Len(value.ToString()) - 1 + + stringChar = value.ToString().Substring(indexChar, 1) + + If stringChar.ToLower() Like "[aeiou]" Then + + ' it is a vowel, increment the count + countOfVowels = countOfVowels + 1 + End If + Next + End Sub + + + Public Sub Merge(ByVal value As CountVowels) + + Accumulate(value.Terminate()) + End Sub + + + Public Function Terminate() As SqlString + + Return countOfVowels.ToString() + End Function +End Structure +' + +'------------------------------------------------------------------------------ +' + +Public Class SampleAggregate + '... +End Class +' diff --git a/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb new file mode 100644 index 0000000000..effdf6bda4 --- /dev/null +++ b/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlUserDefinedTypeAttribute_Type1.vb @@ -0,0 +1,116 @@ +'------------------------------------------------------------------------------ +' +Imports System.Data.SqlTypes +Imports Microsoft.SqlServer.Server + + + +Public Structure Point + Implements INullable + + Private m_x As Integer + Private m_y As Integer + Private is_Null As Boolean + + Public Property X() As Integer + Get + Return (Me.m_x) + End Get + Set(ByVal Value As Integer) + m_x = Value + End Set + End Property + + Public Property Y() As Integer + Get + Return (Me.m_y) + End Get + Set(ByVal Value As Integer) + m_y = Value + End Set + End Property + + Public ReadOnly Property IsNull() As Boolean Implements INullable.IsNull + Get + Return is_Null + End Get + End Property + + Public Shared ReadOnly Property Null() As Point + Get + Dim pt As Point = New Point + pt.is_Null = True + Return pt + End Get + End Property + + Public Overrides Function ToString() As String + If Me.IsNull() Then + Return Nothing + Else + Return Me.m_x & ":" & Me.m_y + End If + End Function + + Public Shared Function Parse(ByVal s As SqlString) As Point + If s = SqlString.Null Then + Return Null + End If + + If s.ToString() = SqlString.Null.ToString() Then + Return Null + End If + + If s.IsNull Then + Return Null + End If + + 'Parse input string here to separate out coordinates + Dim str As String = Convert.ToString(s) + Dim xy() As String = str.Split(":"c) + + Dim pt As New Point() + pt.X = CType(xy(0), Integer) + pt.Y = CType(xy(1), Integer) + Return (pt) + End Function + + Public Function Quadrant() As SqlString + + If m_x = 0 And m_y = 0 Then + Return "centered" + End If + + Dim stringResult As String = "" + + Select Case m_x + Case 0 + stringResult = "center" + Case Is > 0 + stringResult = "right" + Case Is < 0 + stringResult = "left" + End Select + + Select Case m_y + Case 0 + stringResult = stringResult & " center" + Case Is > 0 + stringResult = stringResult & " top" + Case Is < 0 + stringResult = stringResult & " bottom" + End Select + + Return stringResult + End Function +End Structure +' + +'------------------------------------------------------------------------------ +' + +Public Class SampleType + + '... +End Class +' diff --git a/doc/samples/SqlConnection_BeginTransaction.cs b/doc/samples/SqlConnection_BeginTransaction.cs index 9665c33c12..13f9c0414d 100644 --- a/doc/samples/SqlConnection_BeginTransaction.cs +++ b/doc/samples/SqlConnection_BeginTransaction.cs @@ -24,7 +24,7 @@ private static void ExecuteSqlTransaction(string connectionString) SqlTransaction transaction; // Start a local transaction. - transaction = connection.BeginTransaction("SampleTransaction"); + transaction = connection.BeginTransaction(); // Must assign both transaction object and connection // to Command object for a pending local transaction diff --git a/doc/samples/SqlConnection_BeginTransaction2.cs b/doc/samples/SqlConnection_BeginTransaction2.cs index 9665c33c12..1be9f5987d 100644 --- a/doc/samples/SqlConnection_BeginTransaction2.cs +++ b/doc/samples/SqlConnection_BeginTransaction2.cs @@ -52,7 +52,7 @@ private static void ExecuteSqlTransaction(string connectionString) // Attempt to roll back the transaction. try { - transaction.Rollback(); + transaction.Rollback("SampleTransaction"); } catch (Exception ex2) { diff --git a/doc/samples/SqlConnection_BeginTransaction3.cs b/doc/samples/SqlConnection_BeginTransaction3.cs index 44eb020ff7..c68284f6d9 100644 --- a/doc/samples/SqlConnection_BeginTransaction3.cs +++ b/doc/samples/SqlConnection_BeginTransaction3.cs @@ -47,7 +47,7 @@ private static void ExecuteSqlTransaction(string connectionString) { try { - transaction.Rollback(); + transaction.Rollback("SampleTransaction"); } catch (SqlException ex) { diff --git a/doc/samples/SqlDataSourceEnumeratorExample.cs b/doc/samples/SqlDataSourceEnumeratorExample.cs new file mode 100644 index 0000000000..279881c672 --- /dev/null +++ b/doc/samples/SqlDataSourceEnumeratorExample.cs @@ -0,0 +1,33 @@ +using System; +// +using Microsoft.Data.Sql; + +class Program +{ + static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +} +// diff --git a/doc/samples/SqlDataSourceEnumeratorVersionExample.cs b/doc/samples/SqlDataSourceEnumeratorVersionExample.cs new file mode 100644 index 0000000000..73eefc1235 --- /dev/null +++ b/doc/samples/SqlDataSourceEnumeratorVersionExample.cs @@ -0,0 +1,25 @@ +using System; +// +using Microsoft.Data.Sql; + +class Program +{ + static void Main() + { + // Retrieve the enumerator instance, and + // then retrieve the data sources. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Filter the sources to just show SQL Server 2012 instances. + System.Data.DataRow[] rows = table.Select("Version LIKE '11%'"); + foreach (System.Data.DataRow row in rows) + { + Console.WriteLine(row["ServerName"]); + } + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } +} +// diff --git a/doc/samples/SqlFunctionAttribute.cs b/doc/samples/SqlFunctionAttribute.cs index 6f20986cf6..28a027caad 100644 --- a/doc/samples/SqlFunctionAttribute.cs +++ b/doc/samples/SqlFunctionAttribute.cs @@ -2,7 +2,7 @@ using System.IO; using System.Collections; // -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; public class Class1 { diff --git a/doc/samples/SqlUserDefinedAggregate.cs b/doc/samples/SqlUserDefinedAggregate.cs index 45a45dff7c..7f3792c133 100644 --- a/doc/samples/SqlUserDefinedAggregate.cs +++ b/doc/samples/SqlUserDefinedAggregate.cs @@ -1,20 +1,20 @@ using System; // -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; using System.IO; using System.Data.Sql; using System.Data.SqlTypes; using System.Text; [Serializable] -[Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregate( - Microsoft.Data.SqlClient.Server.Format.UserDefined, +[Microsoft.SqlServer.Server.SqlUserDefinedAggregate( + Microsoft.SqlServer.Server.Format.UserDefined, IsInvariantToNulls = true, IsInvariantToDuplicates = false, IsInvariantToOrder = false, MaxByteSize = 8000) ] -public class Concatenate : Microsoft.Data.SqlClient.Server.IBinarySerialize +public class Concatenate : Microsoft.SqlServer.Server.IBinarySerialize { public void Read(BinaryReader r) diff --git a/doc/samples/SqlUserDefinedType1.cs b/doc/samples/SqlUserDefinedType1.cs index 5601a016b1..8f440648c2 100644 --- a/doc/samples/SqlUserDefinedType1.cs +++ b/doc/samples/SqlUserDefinedType1.cs @@ -2,7 +2,7 @@ // using System; using System.Data.SqlTypes; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable()] [SqlUserDefinedType(Format.Native)] @@ -133,7 +133,7 @@ public SqlString Quadrant() //----------------------------------------------------------------------------- // -// using Microsoft.Data.SqlClient.Server; +// using Microsoft.SqlServer.Server; [SqlUserDefinedType(Format.Native, MaxByteSize = 8000)] public class SampleType diff --git a/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml b/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml new file mode 100644 index 0000000000..55e1f2c057 --- /dev/null +++ b/doc/snippets/Microsoft.Data.Sql/SqlDataSourceEnumerator.xml @@ -0,0 +1,65 @@ + + + + + Provides a mechanism for enumerating all available instances of SQL Server within the local network. + + class exposes this information to the application developer, providing a containing information about all the available servers. This returned table contains a list of server instances that matches the list provided when a user attempts to create a new connection, and on the `Connection Properties` dialog box, expands the drop-down list containing all the available servers. + + ]]> + + Enumerating Instances of SQL Server + + + Retrieves a containing information about all visible SQL Server instances. + A containing information about the visible SQL Server instances. + +
10.0.xx for SQL Server 2008
10.50.x for SQL Server 2008 R2
11.0.xx for SQL Server 2012
12.0.xx for SQL Server 2014
13.0.xx for SQL Server 2016
14.0.xx for SQL Server 2017| + +> [!NOTE] +> Due to the nature of the mechanism used by to locate data sources on a network, the method will not always return a complete list of the available servers, and the list might not be the same on every call. If you plan to use this function to let users select a server from a list, make sure that you always also supply an option to type in a name that is not in the list, in case the server enumeration does not return all the available servers. In addition, this method may take a significant amount of time to execute, so be careful about calling it when performance is critical. + +## Examples + The following console application retrieves information about all the visible SQL Server instances and displays the information in the console window. + +[!code-csharp[SqlDataSourceEnumerator.Example#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorExample.cs#1)] + + ]]>
+
+ Enumerating Instances of SQL Server +
+ + Gets an instance of the , which can be used to retrieve information about available SQL Server instances. + An instance of the used to retrieve information about available SQL Server instances. + + class does not provide a constructor. Use the property to retrieve an instance of the class instead. + +[!code-csharp[SqlDataSourceEnumeratorExample#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorExample.cs#1)] + +## Examples +The following console application displays a list of all the available SQL Server 2005 instances within the local network. This code uses the method to filter the rows in the table returned by the method. + [!code-csharp[SqlDataSourceEnumeratorVersionExample#1](~/../sqlclient/doc/samples/SqlDataSourceEnumeratorVersionExample.cs#1)] + + ]]> + + Enumerating Instances of SQL Server + + +
+
diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml deleted file mode 100644 index d429e11dd9..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/DataAccessKind.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Describes the type of access to user data for a user-defined method or function. - - and to indicate whether the method or function uses ADO.NET to connect back to the database using the "context connection." - - Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function perform read-only data-access operations, such as executing SELECT statements). - - ]]> - - - - The method or function does not access user data. - - - The method or function reads user data. - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml deleted file mode 100644 index a84e479859..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/IBinarySerialize.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - Provides custom implementation for user-defined type (UDT) and user-defined aggregate serialization and deserialization. - - .`Native` or .`UserDefined`. - - .`Native` allows SQL Server to handle serialization and deserialization automatically, but the format has restrictions on the kind of types it can handle. .`UserDefined` allows user-defined types and aggregates to handle their own serialization. User-defined types and aggregates must be marked with .`UserDefined` in the `SqlUserDefinedType` or `SqlUserDefinedAggregate` attribute, and must implement the interface. - - Note that even with custom serialization, the total size of each instance must be under the maximum allowed limit, currently 8000 bytes. - - ]]> - - - - The stream from which the object is deserialized. - Generates a user-defined type (UDT) or user-defined aggregate from its binary form. - - method must reconstitute your object using the information written by the method. - -## Examples - The following example shows the implementation of the method of a UDT, which uses a to de-serialize a previously persisted UDT. This example assumes that the UDT has two data properties: `StringValue` and `DoubleValue`. - - [!code-csharp[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/IBinarySerialize.cs#1)] - - ]]> - - - - The stream to which the UDT or user-defined aggregate is serialized. - Converts a user-defined type (UDT) or user-defined aggregate into its binary format so that it may be persisted. - - method to reconstitute your UDT or user-defined aggregate. - -## Examples - The following example shows the implementation of the method of a UDT, which uses a to serialize the UDT in the user-defined binary format. The purpose of the null character padding is to ensure that the string value is completely separated from the double value, so that one UDT is compared to another in Transact-SQL code, string bytes are compared to string bytes and double bytes are compared to double bytes. - - [!code-csharp[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/IBinarySerialize.cs#2)] - - ]]> - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml deleted file mode 100644 index 70bfd160f4..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/InvalidUdtException.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Thrown when SQL Server or the ADO.NET provider detects an invalid user-defined type (UDT). - To be added. - - - The object. - The object. - Streams all the properties into the class for the given . - - class to make the class serializable. - - ]]> - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml deleted file mode 100644 index 14bc23a2bd..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedTypeAttribute.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Used to mark a type definition in an assembly as a user-defined type (UDT) in SQL Server. The properties on the attribute reflect the physical characteristics used when the type is registered with SQL Server. This class cannot be inherited. - - custom attribute. Every UDT must be annotated with this attribute. See [CLR User-Defined Types](https://go.microsoft.com/fwlink/?LinkId=128028) for more information about UDTs, including an example of a UDT. - -## Examples -The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. - -[!code-csharp[SqlUserDefinedType Example#1](~/../sqlclient/doc/samples/SqlUserDefinedType.cs#1)] - -]]> - - - - One of the values representing the serialization format of the type. - A required attribute on a user-defined type (UDT), used to confirm that the given type is a UDT and to indicate the storage format of the UDT. - - - - - - The serialization format as a . - A value representing the serialization format. - - - Indicates whether the user-defined type is byte ordered. - - if the user-defined type is byte ordered; otherwise . - - property in effect guarantees that the serialized binary data can be used for semantic ordering of the information. Thus, each instance of a byte-ordered UDT object can only have one serialized representation. When a comparison operation is performed in SQL Server on the serialized bytes, its results should be the same as if the same comparison operation had taken place in managed code. - -The following features are supported when is set to `true`: - -- The ability to create indexes on columns of this type. - -- The ability to create primary and foreign keys as well as CHECK and UNIQUE constraints on columns of this type. - -- The ability to use Transact-SQL ORDER BY, GROUP BY, and PARTITION BY clauses. In these cases, the binary representation of the type is used to determine the order. - -- The ability to use comparison operators in Transact-SQL statements. - -- The ability to persist computed columns of this type. - -Note that both the `Native` and `UserDefined` serialization formats support the following comparison operators when is set to `true`: - -- Equal to (=) - -- Not equal to (!=) - -- Greater than (>) - -- Less than (\<) - -- Greater than or equal to (>=) - -- Less than or equal to (<=) - -]]> - - - - Indicates whether all instances of this user-defined type are the same length. - - if all instances of this type are the same length; otherwise . - - - . This attribute is only relevant for UDTs with `UserDefined` serialization . - -]]> - - - - The maximum size of the instance, in bytes. - An value representing the maximum size of the instance. - - property with the `UserDefined` serialization . - -When connecting to SQL Server 2005 or earlier, must be between 1 and 8000. - -When connecting to SQL Server 2008 or later, set between 1 and 8000, for a type whose instances are always 8,000 bytes or less. For types that can have instances larger than 8000, specify -1. - -For a UDT with user-defined serialization specified, refers to the total size of the UDT in its serialized form as defined by the user. Consider a UDT with a property of a string of 10 characters (). When the UDT is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized UDT must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. - -This property should not be used with `Native` serialization . - -]]> - - - - The SQL Server name of the user-defined type. - A value representing the SQL Server name of the user-defined type. - - property is not used within SQL Server, but is used by the Microsoft Visual Studio .NET Integrated Development Environment (IDE). - -]]> - - - - The name of the method used to validate instances of the user-defined type. - A representing the name of the method used to validate instances of the user-defined type. - - - - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml deleted file mode 100644 index e2209278d7..0000000000 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SystemDataAccessKind.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Describes the type of access to system data for a user-defined method or function. - - and to indicate what type of access to system data the method or function has. - - Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function performs read-only data-access operations, such as executing SELECT statements). - - ]]> - - - - The method or function does not access system data. - - - The method or function reads system data. - - - diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml index e013016ee9..01d30ee8d3 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlClientMetaDataCollectionNames.xml @@ -60,5 +60,9 @@ A constant for use with the **GetSchema** method that represents the **ColumnSetColumns** collection. To be added. + + A constant for use with the **GetSchema** method that represents the **StructuredTypeMembers** collection. + To be added. + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml index 62ae7811a8..6aac0a9d42 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml @@ -2904,6 +2904,9 @@ To apply the retry logic, do the following steps before executing the command: > [!NOTE] > The default retry logic provider is not enabled unless it is configured in an application configuration file. For more information, see [Configurable retry logic configuration file](/sql/connect/ado-net/configurable-retry-logic-config-file-sqlclient). +> [!CAUTION] +> A command with isn't compatible with the built-in retry logic. The underlying connection is immediately closed after the first execution attempt and is no longer available for subsequent retries. + ## Example The following sample creates a database and establishes an active connection to it. While the database has an active connection, it tries to drop it with a new and a that uses a . You should kill the active connection through the database to unblock the second command before exceeding the number of retries. The blocking connection simulates a situation like a command still running in the database and unlikely to finish. diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml index ae9ed935b5..d139d0e1ec 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml @@ -527,8 +527,8 @@ End Module |Application Intent

-or-

ApplicationIntent|ReadWrite|Declares the application workload type when connecting to a server. Possible values are `ReadOnly` and `ReadWrite`. For example:

`ApplicationIntent=ReadOnly`

For more information about SqlClient support for Always On Availability Groups, see [SqlClient Support for High Availability, Disaster Recovery](/sql/connect/ado-net/sql/sqlclient-support-high-availability-disaster-recovery).| |Application Name|N/A|The name of the application. If no application name is provided, 'Framework Microsoft SqlClient Data Provider' when running on .NET Framework and 'Core Microsoft SqlClient Data Provider' otherwise.

An application name can be 128 characters or less.| |AttachDBFilename

-or-

Extended Properties

-or-

Initial File Name|N/A|The name of the primary database file, including the full path name of an attachable database. AttachDBFilename is only supported for primary data files with an .mdf extension.

If the value of the AttachDBFileName key is specified in the connection string, the database is attached and becomes the default database for the connection.

If this key is not specified and if the database was previously attached, the database will not be reattached. The previously attached database will be used as the default database for the connection.

If this key is specified together with the AttachDBFileName key, the value of this key will be used as the alias. However, if the name is already used in another attached database, the connection will fail.

The path may be absolute or relative by using the DataDirectory substitution string. If DataDirectory is used, the database file must exist within a subdirectory of the directory pointed to by the substitution string. **Note:** Remote server, HTTP, and UNC path names are not supported.

The database name must be specified with the keyword 'database' (or one of its aliases) as in the following:

"AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase"

An error will be generated if a log file exists in the same directory as the data file and the 'database' keyword is used when attaching the primary data file. In this case, remove the log file. Once the database is attached, a new log file will be automatically generated based on the physical path.| -|Attestation Protocol|N/A|Gets or sets the value of Attestation Protocol.

Valid values are:
`AAS`
`HGS`| -|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Active Directory Service Principal`, `Active Directory Device Code Flow`, `Active Directory Managed Identity`, `Active Directory MSI`, `Active Directory Default`, `Sql Password`.| +|Attestation Protocol|NotSpecified|Gets or sets the value of Attestation Protocol.

When no value is specified, secure enclaves are disabled on the connection.

Valid values are:
`AAS`
`HGS`
`None` (Only valid in v3.1 and v4.1+))| +|Authentication|N/A|The authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities).

Valid values are:

`Active Directory Integrated`, `Active Directory Interactive`, `Active Directory Password`, `Active Directory Service Principal`, `Active Directory Device Code Flow`, `Active Directory Managed Identity`, `Active Directory MSI`, `Active Directory Default`, `Sql Password`.

For additional information see [Using Azure Active Directory authentication with SqlClient](https://docs.microsoft.com/sql/connect/ado-net/sql/azure-active-directory-authentication?view=sql-server-ver15).| |Column Encryption Setting|disabled|Enables or disables [Always Encrypted](/sql/relational-databases/security/encryption/always-encrypted-database-engine) functionality for the connection. Supported values are: `enabled` and `disabled`| |Command Timeout|30|The default wait time (in seconds) before terminating the attempt to execute a command and generating an error.

Valid values are greater than or equal to 0 and less than or equal to 2147483647.| |Connect Retry Count

-or-

ConnectRetryCount|1|Controls the number of reconnection attempts after the client identifies an idle connection failure. Valid values are 0 to 255. The default is 1. 0 means do not attempt to reconnect (disable connection resiliency).

For additional information about idle connection resiliency, see [Technical Article - Idle Connection Resiliency](https://go.microsoft.com/fwlink/?LinkId=393996).| @@ -537,9 +537,11 @@ End Module |Current Language

-or-

Language|N/A|Sets the language used for database server warning or error messages.

The language name can be 128 characters or less.| |Data Source

-or-

Server

-or-

Address

-or-

Addr

-or-

Network Address|N/A|The name or network address of the instance of SQL Server to which to connect. The port number can be specified after the server name:

`server=tcp:servername, portnumber`

When specifying a local instance, always use (local). To force a protocol, add one of the following prefixes:

`np:(local), tcp:(local), lpc:(local)`

You can also connect to a LocalDB database as follows:

`server=(localdb)\\myInstance`

For more information about LocalDB, see [SqlClient Support for LocalDB](/sql/connect/ado-net/sql/sqlclient-support-localdb).

**Data Source** must use the TCP format or the Named Pipes format.

TCP format is as follows:

- tcp:\\\
- tcp:\,\

The TCP format must start with the prefix "tcp:" and is followed by the database instance, as specified by a host name and an instance name. This format is not applicable when connecting to Azure SQL Database. TCP is automatically selected for connections to Azure SQL Database when no protocol is specified.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The instance name is used to resolve to a particular TCP/IP port number on which a database instance is hosted. Alternatively, specifying a TCP/IP port number directly is also allowed. If both instance name and port number are not present, the default database instance is used.

The Named Pipes format is as follows:

- np:\\\\\pipe\\

The Named Pipes format MUST start with the prefix "np:" and is followed by a named pipe name.

The host name MUST be specified in one of the following ways:

- NetBIOSName
- IPv4Address
- IPv6Address

The pipe name is used to identify the database instance to which the .NET application will connect.

If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" should not be specified. **Note:** You can force the use of TCP instead of shared memory, either by prefixing **tcp:** to the server name in the connection string, or by using **localhost**.| |Enclave Attestation Url|N/A|Gets or sets the enclave attestation URL to be used with enclave based Always Encrypted.| -|Encrypt|'true'|When `true`, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).

When `TrustServerCertificate` is false and `Encrypt` is true, the server name (or IP address) in a SQL Server SSL certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Accepted wildcards used by server certificates for server authentication](https://support.microsoft.com/kb/258858).| +|Encrypt|'true' in 4.0 and above

'false' in 3.x and below|Recognized values are:
versions 1 - 4: `true`/`yes` and `false`/`no`
versions 5+: `true`/`yes`/`mandatory`, `false`/`no`/`optional` and `strict`. When `true`, TLS encryption is used for all data sent between the client and server if the server has a certificate installed. When `strict`, TDS 8.0 TLS encryption is used and the `TrustServerCertificate` setting is ignored and treated as false. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).

When `Encrypt` is `mandatory` or `strict` and `TrustServerCertificate` is `false`, the server name (or IP address) in a server's certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. | |Enlist|'true'|`true` indicates that the SQL Server connection pooler automatically enlists the connection in the creation thread's current transaction context.| |Failover Partner|N/A|The name of the failover partner server where database mirroring is configured.

If the value of this key is "", then **Initial Catalog** must be present, and its value must not be "".

The server name can be 128 characters or less.

If you specify a failover partner but the failover partner server is not configured for database mirroring and the primary server (specified with the Server keyword) is not available, then the connection will fail.

If you specify a failover partner and the primary server is not configured for database mirroring, the connection to the primary server (specified with the Server keyword) will succeed if the primary server is available.| +|Failover Partner SPN

-or-

FailoverPartnerSPN|N/A|The SPN for the failover partner. The default value is an empty string, which causes SqlClient to use the default, driver-generated SPN.

(Only available in v5.0+)| +|Host Name In Certificate

-or-

HostNameInCertificate|N/A|The host name to use when validating the server certificate. When not specified, the server name from the Data Source is used for certificate validation.

(Only available in v5.0+)| |Initial Catalog

-or-

Database|N/A|The name of the database.

The database name can be 128 characters or less.| |Integrated Security

-or-

Trusted_Connection|'false'|When `false`, User ID and Password are specified in the connection. When `true`, the current Windows account credentials are used for authentication.

Recognized values are `true`, `false`, `yes`, `no`, and `sspi` (strongly recommended), which is equivalent to `true`.

If User ID and Password are specified and Integrated Security is set to true, the User ID and Password will be ignored and Integrated Security will be used.

is a more secure way to specify credentials for a connection that uses SQL Server Authentication (`Integrated Security=false`).| |IP Address Preference

-or-

IPAddressPreference|IPv4First|The IP address family preference when establishing TCP connections. If `Transparent Network IP Resolution` (in .NET Framework) or `Multi Subnet Failover` is set to true, this setting has no effect. Supported values include:

`IPAddressPreference=IPv4First`

`IPAddressPreference=IPv6First`

`IPAddressPreference=UsePlatformDefault`| @@ -551,15 +553,16 @@ End Module |Network Library

-or-

Network

-or-

Net|N/A|The network library used to establish a connection to an instance of SQL Server. Supported values include:

dbnmpntw (Named Pipes)

dbmsrpcn (Multiprotocol, Windows RPC)

dbmsadsn (Apple Talk)

dbmsgnet (VIA)

dbmslpcn (Shared Memory)

dbmsspxn (IPX/SPX)

dbmssocn (TCP/IP)

Dbmsvinn (Banyan Vines)

The corresponding network DLL must be installed on the system to which you connect. If you do not specify a network and you use a local server (for example, "." or "(local)"), shared memory is used. In this example, the network library is Win32 Winsock TCP/IP (dbmssocn), and 1433 is the port being used.

`Network Library=dbmssocn;Data Source=000.000.000.000,1433;`| |Packet Size|8000|Size in bytes of the network packets used to communicate with an instance of SQL Server.

The packet size can be greater than or equal to 512 and less than or equal to 32768.| |Password

-or-

PWD|N/A|The password for the SQL Server account logging on. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keyword instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The password must be 128 characters or less.| -|Persist Security Info

-or-

PersistSecurityInfo|'false'|When set to `false` or `no` (strongly recommended), security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. Resetting the connection string resets all connection string values including the password. Recognized values are `true`, `false`, `yes`, and `no`.| +|Persist Security Info

-or-

PersistSecurityInfo|'false'|When set to `false` or `no` (strongly recommended), security-sensitive information, such as the password or access token, is not returned as part of the connection if the connection is open or has ever been in an open state. This property should only be set to `true` if your application has a specific need to read the password out of an already-opened database connection. The default value of `false` is the more secure setting; using `true` for this property opens your application to security risks such as accidentally logging or tracing the database password.

Resetting the connection string resets all connection string values including the password. Recognized values are `true`, `false`, `yes`, and `no`.| |Pool Blocking Period

-or-

PoolBlockingPeriod|Auto|Sets the blocking period behavior for a connection pool. See property for details.| |Pooling|'true'|When the value of this key is set to true, any newly created connection will be added to the pool when closed by the application. In a next attempt to open the same connection, that connection will be drawn from the pool.

Connections are considered the same if they have the same connection string. Different connections have different connection strings.

The value of this key can be "true", "false", "yes", or "no".| |Replication|'false'|`true` if replication is supported using the connection.| +|Server SPN

-or-

ServerSPN|N/A|The SPN for the data source. The default value is an empty string, which causes SqlClient to use the default, driver-generated SPN.

(Only available in v5.0+)| |Transaction Binding|Implicit Unbind|Controls connection association with an enlisted `System.Transactions` transaction.

Possible values are:

`Transaction Binding=Implicit Unbind;`

`Transaction Binding=Explicit Unbind;`

Implicit Unbind causes the connection to detach from the transaction when it ends. After detaching, additional requests on the connection are performed in autocommit mode. The `System.Transactions.Transaction.Current` property is not checked when executing requests while the transaction is active. After the transaction has ended, additional requests are performed in autocommit mode.

If the system ends the transaction (in the scope of a using block) before the last command completes, it will throw .

Explicit Unbind causes the connection to remain attached to the transaction until the connection is closed or an explicit `SqlConnection.TransactionEnlist(null)` is called. Beginning in .NET Framework 4.0, changes to Implicit Unbind make Explicit Unbind obsolete. An `InvalidOperationException` is thrown if `Transaction.Current` is not the enlisted transaction or if the enlisted transaction is not active.| |Transparent Network IP Resolution

-or-

TransparentNetworkIPResolution|See description.|When the value of this key is set to `true`, the application is required to retrieve all IP addresses for a particular DNS entry and attempt to connect with the first one in the list. If the connection is not established within 0.5 seconds, the application will try to connect to all others in parallel. When the first answers, the application will establish the connection with the respondent IP address.

If the `MultiSubnetFailover` key is set to `true`, `TransparentNetworkIPResolution` is ignored.

If the `Failover Partner` key is set, `TransparentNetworkIPResolution` is ignored.

The value of this key must be `true`, `false`, `yes`, or `no`.

A value of `yes` is treated the same as a value of `true`.

A value of `no` is treated the same as a value of `false`.

The default values are as follows:

  • `false` when:

    • Connecting to Azure SQL Database where the data source ends with:

      • .database.chinacloudapi.cn
      • .database.usgovcloudapi.net
      • .database.cloudapi.de
      • .database.windows.net
    • `Authentication` is 'Active Directory Password' or 'Active Directory Integrated'
  • `true` in all other cases.
| -|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, SSL is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).| +|Trust Server Certificate

-or-

TrustServerCertificate|'false'|When set to `true`, TLS is used to encrypt the channel when bypassing walking the certificate chain to validate trust. If TrustServerCertificate is set to `true` and Encrypt is set to `false`, the channel is not encrypted. Recognized values are `true`, `false`, `yes`, and `no`. For more information, see [Connection String Syntax](/sql/connect/ado-net/connection-string-syntax).| |Type System Version|N/A|A string value that indicates the type system the application expects. The functionality available to a client application is dependent on the version of SQL Server and the compatibility level of the database. Explicitly setting the type system version that the client application was written for avoids potential problems that could cause an application to break if a different version of SQL Server is used. **Note:** The type system version cannot be set for common language runtime (CLR) code executing in-process in SQL Server. For more information, see [SQL Server Common Language Runtime Integration](/dotnet/framework/data/adonet/sql/sql-server-common-language-runtime-integration).

Possible values are:

`Type System Version=SQL Server 2012;`

`Type System Version=SQL Server 2008;`

`Type System Version=SQL Server 2005;`

`Type System Version=Latest;`

`Type System Version=SQL Server 2012;` specifies that the application will require version 11.0.0.0 of Microsoft.SqlServer.Types.dll. The other `Type System Version` settings will require version 10.0.0.0 of Microsoft.SqlServer.Types.dll.

`Latest` is obsolete and should not be used. `Latest` is equivalent to `Type System Version=SQL Server 2008;`.| -|User ID

-or-

UID

-or-|N/A|The SQL Server login account. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keywords instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The user ID must be 128 characters or less.| +|User ID

-or-

UID

-or-

User|N/A|The SQL Server login account. Not recommended. To maintain a high level of security, we strongly recommend that you use the `Integrated Security` or `Trusted_Connection` keywords instead. is a more secure way to specify credentials for a connection that uses SQL Server Authentication.

The user ID must be 128 characters or less.| |User Instance|'false'|A value that indicates whether to redirect the connection from the default SQL Server Express instance to a runtime-initiated instance running under the account of the caller.| |Workstation ID

-or-

WSID|The local computer name|The name of the workstation connecting to SQL Server.

The ID must be 128 characters or less.| diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml index d5f32ef2d3..db98ad4fdf 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionAttestationProtocol.xml @@ -13,10 +13,10 @@ Attestation portocol for Azure Attestation Service 1 - - Attestation protocol for Simulator + + Attestation protocol for no attestation. Only compatible with Virtualization-based security (VBS) enclaves. An Enclave Attestation Url is not required when using this protocol. 2 - + Attestation protocol for Host Guardian Service 3 diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml new file mode 100644 index 0000000000..6db9533606 --- /dev/null +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml @@ -0,0 +1,75 @@ + + + + + These options are used to control encryption behavior of the communication between the server and the client. + + property. When converting from a boolean, a value of `true` converts to and a value of `false` converts to . When converting to a boolean, and convert to `true` and converts `false`. + + ]]> + + + + + Converts the specified string representation of a logical value to its equivalent. + + A string containing the value to convert. + + An object that is equivalent to contained in . + + + Throws exception if provided is not convertible to type. + + + + + Converts the specified string representation of a logical value to its equivalent and returns a value that indicates whether the conversion succeeded. + + A string containing the value to convert. + + An object that is equivalent to contained in . if conversion fails. + + + if the parameter was converted successfully; otherwise, . + + This method does not throw an exception if conversion fails. + + + Specifies that TLS encryption is optional when connecting to the server. If the server requires encryption, encryption will be negotiated. + + + Specifies that TLS encryption is required when connecting to the server. If the server doesn't support encryption, the connection will fail. + + + Enables and requires TDS 8.0, TLS encryption to the server. If the server doesn't support TDS 8.0, TLS encryption, the connection will fail. + + + The boolean value to be used for implicit comparison. + + Enables implicit converstion of a boolean to a . A value of converts to . A value of converts to . + + + + The value to be used for implicit comparison. + + Enables implicit converstion of a to a boolean. and convert to . converts to . + + + + Returns the string value of . + + + + Compares the representation of to another . + + + + + Returns the hash code of the value. + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml index 5b360dfb97..bb096150eb 100644 --- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml +++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml @@ -168,7 +168,7 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security Gets or sets the value of Attestation Protocol. - Returns the attestation protocol. + The attestation protocol. Gets or sets the authentication method used for [Connecting to SQL Database By Using Azure Active Directory Authentication](https://azure.microsoft.com/documentation/articles/sql-database-aad-authentication/#7-connect-to-your-database-by-using-azure-active-directory-identities). @@ -177,6 +177,9 @@ Modified: Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security @@ -407,18 +410,21 @@ If the value of the **Network** key is specified, the prefixes "tcp:" and "np:" The enclave attestation URL. - Gets or sets a Boolean value that indicates whether SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed. - The value of the property, or if none has been supplied. + Gets or sets a value since version 5.0 or a value for the earlier versions that indicates whether TLS encryption is required for all data sent between the client and server. + The value of the property. , or `true`, the server name (or IP address) in a server's TLS certificate must exactly match the server name (or IP address) specified in the connection string. Otherwise, the connection attempt will fail. For information about support for certificates whose subject starts with a wildcard character (*), see [Enable encrypted connections to the Database Engine](/sql/database-engine/configure-windows/enable-encrypted-connections-to-the-database-engine#certificate-requirements). + +> [!NOTE] +> Starting from **version 4.0**, the default value of the property `Encrypt` is set to `true` while it is `false` for earlier versions. > [!NOTE] -> Starting from **version 4.0**, the default value of the property `Encrypt` is set to `true`. +> Starting from **version 5.0**, the data type is updated to , and the default value of the `Encrypt` property is set to . ]]> @@ -456,6 +462,25 @@ If you specify a failover partner and the primary server is not configured for d ]]> + + Gets or sets the service principal name (SPN) of the failover partner for the connection. + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using Integrated Security mode, otherwise it is ignored. + + ]]> + + + To be added. To be added. @@ -697,7 +722,7 @@ The password must be 128 characters or less. The password was incorrectly set to null. See code sample below. - Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state. + Gets or sets a Boolean value indicating if security-sensitive information, such as the password or access token, should be returned as part of the connection string on a connection created with this after that connection has ever been in an open state. This property should only be set to if your application has a specific need to read the password out of an already-opened database connection. The default value of is the more secure setting; using for this property opens your application to security risks such as accidentally logging or tracing the database password. The value of the property, or if none has been supplied. + + Gets or sets the service principal name (SPN) of the data source. + + The value of the property, or if none has been supplied. + + + + [!NOTE] +> This property only applies when using Integrated Security mode, otherwise it is ignored. + + ]]> + + + The key to locate in the . Indicates whether the specified key exists in this instance. @@ -895,7 +939,7 @@ Database = AdventureWorks ## Remarks This property corresponds to the "Trust Server Certificate" and "TrustServerCertificate" keys within the connection string. - When `Trust Server Certificate` is set to `true`, the transport layer will use SSL to encrypt the channel and bypass walking the certificate chain to validate trust. If `Trust Server Certificate` is set to `true` and encryption is enforced by target server, the encryption level specified on the server will be used even if `Encrypt` is set to `false`. The connection will fail otherwise. + When `Trust Server Certificate` is set to `true`, the transport layer will use TLS to encrypt the channel and bypass walking the certificate chain to validate trust. If `Trust Server Certificate` is set to `true` and encryption is enforced by target server, the encryption level specified on the server will be used even if `Encrypt` is set to `false`. The connection will fail otherwise. For more information, see [Encryption Hierarchy](/sql/relational-databases/security/encryption/encryption-hierarchy) and [Using Encryption Without Validation](/sql/relational-databases/native-client/features/using-encryption-without-validation). diff --git a/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml b/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml new file mode 100644 index 0000000000..1590300ea4 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/DataAccessKind.xml @@ -0,0 +1,26 @@ + + + + + Describes the type of access to user data for a user-defined method or function. + + and to indicate whether the method or function uses ADO.NET to connect back to the database using the "context connection." + +Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function perform read-only data-access operations, such as executing SELECT statements). + + ]]> + + + + The method or function does not access user data. + + + The method or function reads user data. + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml b/doc/snippets/Microsoft.SqlServer.Server/Format.xml similarity index 67% rename from doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml rename to doc/snippets/Microsoft.SqlServer.Server/Format.xml index b15cd6eadb..31b5776207 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/Format.xml @@ -2,39 +2,44 @@ - Used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. + Used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. and to indicate the serialization format of a user-defined type (UDT) or aggregate. Use of the `Native` and `UserDefined` enumeration members has special requirements. + +## Remarks + +This enumeration is used by and to indicate the serialization format of a user-defined type (UDT) or aggregate. Use of the `Native` and `UserDefined` enumeration members has special requirements. + - `Format.Native` The requirements for the `Format.Native` format are: - + - The with a property value of must be applied to the aggregate or UDT if it is defined in a class and not a structure. This controls the physical layout of the data fields and is used to force the members to be laid out sequentially in the order they appear. SQL Server uses this attribute to determine the field order for UDTs with multiple fields. - + - The type must contain at least one member (serialized values cannot be zero bytes in size). - + - All the fields of the aggregate must be *blittable*; that is, they must have a common representation in both managed and unmanaged memory and not require special handling by the interop marshaler. - + - All the fields of the UDT should be of one of the following types that can be serialized: `bool`, `byte`, `sbyte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `float`, `double`, , , , , , , , , or other value types defined by the user that contain fields of one of these types. - The aggregate must not specify a value for `MaxByteSize`. - + - The aggregate must not have any [NonSerialized] fields. - + - Fields must not be marked as an explicit layout (with a of ). - `Format.UserDefined` The requirements for the `Format.UserDefined` format are: - The aggregate must specify a value for `MaxByteSize`. - - - Specify the attribute property. The default value is `false`. - - - If you omit any field in the or methods, the state of that field is not serialized. + + - Specify the attribute property. The default value is `false`. + + - If you omit any field in the or methods, the state of that field is not serialized. + ## Examples + The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. - -[!code-csharp[SqlUserDefinedType Example#1](~/../sqlclient/doc/samples/SqlUserDefinedType.cs#1)] - + +[!code-csharp[SqlUserDefinedTypeAttribute Example#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedTypeAttribute Example#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb#1)] + ]]> @@ -45,7 +50,7 @@ The following example shows the `UserDefinedType` attribute of the Point UDT. T The serialization format is unknown. - This serialization format gives the developer full control over the binary format through the and methods. + This serialization format gives the developer full control over the binary format through the and methods. diff --git a/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml b/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml new file mode 100644 index 0000000000..79128e5655 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/IBinarySerialize.xml @@ -0,0 +1,56 @@ + + + + + Provides custom implementation for user-defined type (UDT) and user-defined aggregate serialization and deserialization. + + .`Native` or .`UserDefined`. + +.`Native` allows SQL Server to handle serialization and deserialization automatically, but the format has restrictions on the kind of types it can handle. .`UserDefined` allows user-defined types and aggregates to handle their own serialization. User-defined types and aggregates must be marked with .`UserDefined` in the `SqlUserDefinedType` or `SqlUserDefinedAggregate` attribute, and must implement the interface. + +Note that even with custom serialization, the total size of each instance must be under the maximum allowed limit, currently 8000 bytes. + + ]]> + + + + The stream from which the object is deserialized. + Generates a user-defined type (UDT) or user-defined aggregate from its binary form. + + method must reconstitute your object using the information written by the method. + +## Examples +The following example shows the implementation of the method of a UDT, which uses a to de-serialize a previously persisted UDT. This example assumes that the UDT has two data properties: `StringValue` and `DoubleValue`. + +[!code-csharp[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs#1)] +[!code-vb[IBinarySerialize Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb#1)] + + ]]> + + + + The stream to which the UDT or user-defined aggregate is serialized. + Converts a user-defined type (UDT) or user-defined aggregate into its binary format so that it may be persisted. + + method to reconstitute your UDT or user-defined aggregate. + +## Examples +The following example shows the implementation of the method of a UDT, which uses a to serialize the UDT in the user-defined binary format. The purpose of the null character padding is to ensure that the string value is completely separated from the double value, so that one UDT is compared to another in Transact-SQL code, string bytes are compared to string bytes and double bytes are compared to double bytes. + +[!code-csharp[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_IBinarySerialize_Sample.cs#2)] +[!code-vb[IBinarySerialize Samples#2](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_IBinarySerialize_Sample.vb#2)] + + ]]> + + + + diff --git a/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml b/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml new file mode 100644 index 0000000000..9ec4002c76 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/InvalidUdtException.xml @@ -0,0 +1,39 @@ + + + + + Thrown when SQL Server or the ADO.NET provider detects an invalid user-defined type (UDT). + To be added. + + + The object. + The object. + Streams all the properties into the class for the given . + + class to make the class serializable. + + ]]> + + + + The object. + The object that represents a string in string resources. The default value is `SqlUdtReason_NoUdtAttribute` which looks up a localized string similar to "no UDT attribute". + Creates a new object. + A new object. + + [!IMPORTANT] +> This function is exposed for backward compatibility and should be used with the default value for the `resourceReason` parameter. + + ]]> + + + + diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml similarity index 56% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml index bbe76cdd22..cae95f8032 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlFacetAttribute.xml @@ -7,14 +7,14 @@ may only be specified on non-void return values. - - is used only to derive information about the return type, and is not intended to be a constraint specification on what can be stored in the type. Thus, if a field has a indicating its size to be 2 characters, then the SQL Server type of the field access expression is of size 2, but assignments into the field are not restricted by this facet. - - The table below captures the matrix of valid values for the various properties for specific field types. In this table, "Y" indicates that the property is valid, and "N" indicates that the property is not valid. - - The specified must be compatible with the field type. If the property is not valid, type registration will report an error if the user specifies a non-default value for the property. The maximum values for and properties are 38. For the property, the value should be in the range of 1-8000 for binary and non-Unicode data, 1-4000 for Unicode data, or -1. All other values are not valid. - + may only be specified on non-void return values. + + is used only to derive information about the return type, and is not intended to be a constraint specification on what can be stored in the type. Thus, if a field has a indicating its size to be 2 characters, then the SQL Server type of the field access expression is of size 2, but assignments into the field are not restricted by this facet. + +The table below captures the matrix of valid values for the various properties for specific field types. In this table, "Y" indicates that the property is valid, and "N" indicates that the property is not valid. + +The specified must be compatible with the field type. If the property is not valid, type registration will report an error if the user specifies a non-default value for the property. The maximum values for and properties are 38. For the property, the value should be in the range of 1-8000 for binary and non-Unicode data, 1-4000 for Unicode data, or -1. All other values are not valid. + |Type|IsFixedLength|MaxSize|Precision|Scale|IsNullable| |----------|-------------------|-------------|---------------|-----------|----------------| ||N|N|N|N|Y| @@ -39,9 +39,9 @@ |Char[]|Y|Y|N|N|Y| ||N|N|N|Y1|N| ||N|N|Y|Y|Y| - - (1) Specifying the scale on a DateTime type will cause the value to be returned to Transact-SQL as a DateTime2 type with the specified scale. - + +(1) Specifying the scale on a DateTime type will cause the value to be returned to Transact-SQL as a DateTime2 type with the specified scale. + ]]> @@ -56,7 +56,7 @@ property is set to 1. +This property must be set to `false` if the property is set to 1. The default value is `false`. @@ -71,8 +71,8 @@ The default value is `false`. @@ -83,12 +83,12 @@ The default value is `false`. @@ -99,10 +99,10 @@ The default value is `false`. property is valid only for numeric types. The property must also be specified when setting the property. - - The maximum value of the property is 38; the default value is 38. - +The property is valid only for numeric types. The property must also be specified when setting the property. + +The maximum value of the property is 38; the default value is 38. + ]]> @@ -113,10 +113,10 @@ The default value is `false`. property is valid only for decimal types. The property must also be specified when setting the property. - - The maximum value of the property is 38; the default value is 0. - +The property is valid only for decimal types. The property must also be specified when setting the property. + +The maximum value of the property is 38; the default value is 0. + ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml similarity index 62% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml index 7221363628..ddf4c76e4b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlFunctionAttribute.xml @@ -9,26 +9,27 @@ ## Examples The following example shows an aggregate function that returns a list of files in the specified directory path. -[!code-csharp[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/SqlFunctionAttribute.cs#1)] +[!code-csharp[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlFunctionAttribute_Sample.cs#1)] +[!code-vb[SqlFunctionAttribute Sample#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlFunctionAttribute_Sample.vb#1)] ]]> - An optional attribute on a user-defined aggregate, used to indicate that the method should be registered in SQL Server as a function. Also used to set the , , , , , , and properties of the function attribute. + An optional attribute on a user-defined aggregate, used to indicate that the method should be registered in SQL Server as a function. Also used to set the , , , , , , and properties of the function attribute. To be added. Indicates whether the function involves access to user data stored in the local instance of SQL Server. - .: Does not access data. .: Only reads user data. + .: Does not access data. .: Only reads user data. . is also required when connecting to remote servers if transactions integration is required (the default). +The default is . is also required when connecting to remote servers if transactions integration is required (the default). -If a Transact-SQL query is executed from inside a table-valued function (TVF), the property should be set. +If a Transact-SQL query is executed from inside a table-valued function (TVF), the property should be set. ]]> @@ -47,11 +48,11 @@ If a Transact-SQL query is executed from inside a table-valued function (TVF), t ## Remarks A user-defined function is said to be deterministic if it always produces the same output values given the same input values and the same database state. -The property is also useful for indexing the result of the function in the form of indexed computed columns and indexed views. If this property is not specified, the function is assumed to be non-deterministic. +The property is also useful for indexing the result of the function in the form of indexed computed columns and indexed views. If this property is not specified, the function is assumed to be non-deterministic. -Functions that access local data can be deterministic. The data access characteristic is captured separately by the and properties. +Functions that access local data can be deterministic. The data access characteristic is captured separately by the and properties. -Note that data access to remote servers (for example, using a to connect to another SQL Server instance) is available in user-defined functions. However, you must still honor the declaration. If the common language runtime (CLR) function is marked as deterministic, it should not cause side-effects in the remote server. While side-effects against the context connection are restricted, SQL Server will not enforce the restriction for side-effects over remote connections. +Note that data access to remote servers (for example, using a to connect to another SQL Server instance) is available in user-defined functions. However, you must still honor the declaration. If the common language runtime (CLR) function is marked as deterministic, it should not cause side-effects in the remote server. While side-effects against the context connection are restricted, SQL Server will not enforce the restriction for side-effects over remote connections. The default value of this attribute is `false`. @@ -84,22 +85,24 @@ The default value of this attribute is `false`. ## Remarks This attribute is used only by Microsoft Visual Studio to automatically register the specified method as a user-defined function. It is not used by SQL Server. -Thee following example specifies that the user-defined function is referenced using the name `sp_scalarFunc`. +The following example specifies that the user-defined function is referenced using the name `sp_scalarFunc`. ## Examples -[!code-csharp[SqlFunction#10](~/../sqlclient/doc/samples/SqlFunction.cs#10)] +[!code-csharp[SqlFunctionAttribute Samples#10](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs#10)] +[!code-vb[SqlFunctionAttribute Samples#10](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb#10)] + ]]> Indicates whether the function requires access to data stored in the system catalogs or virtual system tables of SQL Server. - .: Does not access system data. .: Only reads system data. + .: Does not access system data. .: Only reads system data. . +The default is . ]]> @@ -116,7 +119,9 @@ This attribute is used only by Microsoft Visual Studio to automatically register The following example specifies that the user-defined function is referenced using the name `sp_tableFunc`. The `TableDefinition` property has the value `letter nchar(1)`. ## Examples -[!code-csharp[SqlFunction#11](~/../sqlclient/doc/samples/SqlFunction.cs#11)] + +[!code-csharp[SqlFunctionAttribute Samples#11](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlFunctionAttribute_SqlFunction.cs#11)] +[!code-vb[SqlFunctionAttribute Samples#11](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/SqlFunctionAttribute_SqlFunction.vb#11)] ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml similarity index 63% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml index 95f73cda49..55a9858ded 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlMethodAttribute.xml @@ -7,14 +7,14 @@ should be used on the setter or the getter directly. +For a property, the should be used on the setter or the getter directly. - inherits from a , so inherits the `FillRowMethodName` and `TableDefinition` fields from . Note that it is not possible to write a table-valued method, although the names of these fields might suggest that it is possible. + inherits from a , so inherits the `FillRowMethodName` and `TableDefinition` fields from . Note that it is not possible to write a table-valued method, although the names of these fields might suggest that it is possible. ## Examples The following example shows a UDT method that is attributed to indicate that the method will not be invoked on null instances of the type, that the method will not change the state of the type, and that the method will not be called when `null` parameters are supplied to the method invocation. -[!code-csharp[SqlMethod Sample#1](~/../sqlclient/doc/samples/SqlMethod.cs#1)] +[!code-csharp[SqlMethodAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/SqlMethod.cs#1)] ]]> @@ -43,11 +43,11 @@ The default value of the `InvokeIfReceiverIsNull` property is `false`. That is, property is set to `true` and the return type of the method is `void`, SQL Server marks the method as a mutator. A mutator method is one that causes a state change in the UDT instance. Mutator methods can be called in assignment statements or data modification statements, but cannot be used in queries. If a method is marked as a mutator but does not return void, then CREATE TYPE does not fail with an error. Even though a returned value other than `void` does not raise an error, the returned value is not accessible and cannot be used. +If the property is set to `true` and the return type of the method is `void`, SQL Server marks the method as a mutator. A mutator method is one that causes a state change in the UDT instance. Mutator methods can be called in assignment statements or data modification statements, but cannot be used in queries. If a method is marked as a mutator but does not return void, then CREATE TYPE does not fail with an error. Even though a returned value other than `void` does not raise an error, the returned value is not accessible and cannot be used. -The default value of the property is `false`. +The default value of the property is `false`. -A property can be a mutator if is used on the setter and is set to `true`. However, a property setter is implicitly treated as a mutator, so it is not necessary to set the property of the to `true`. +A property can be a mutator if is used on the setter and is set to `true`. However, a property setter is implicitly treated as a mutator, so it is not necessary to set the property of the to `true`. ]]> @@ -59,7 +59,7 @@ A property can be a mutator if property is `true`. +The default value of the property is `true`. ]]> diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml similarity index 70% rename from doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml rename to doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml index 5822bac69d..a0a5eafe1b 100644 --- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.xml @@ -7,30 +7,32 @@ custom attribute. Every user-defined aggregate must be annotated with this attribute. +SQL Server creates a user-defined aggregate that is bound to the class definition that has the custom attribute. Every user-defined aggregate must be annotated with this attribute. See "CLR User-Defined Aggregates" in SQL Server 2005 Books Online for more information on user-defined aggregates and examples. ## Examples -The following example shows the attribute for a user-defined aggregate. The aggregate uses custom serialization, has a maximum size of 8000 bytes when serialized, and is invariant to nulls, duplicates, and order. +The following example shows the attribute for a user-defined aggregate. The aggregate uses custom serialization, has a maximum size of 8000 bytes when serialized, and is invariant to nulls, duplicates, and order. -[!code-csharp[SqlUserDefinedAggregate Sample#1](~/../sqlclient/doc/samples/SqlUserDefinedAggregate.cs#1)] +[!code-csharp[SqlUserDefinedAggregateAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedAggregateAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedAggregateAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedAggregateAttribute_Sample.vb#1)] ]]> - One of the values representing the serialization format of the aggregate. + One of the values representing the serialization format of the aggregate. A required attribute on a user-defined aggregate, used to indicate that the given type is a user-defined aggregate and the storage format of the user-defined aggregate. - The serialization format as a . - A representing the serialization format. + The serialization format as a . + A representing the serialization format. @@ -102,13 +104,13 @@ Incorrectly setting this property can result in incorrect query results. This pr ## Remarks This property does not have to be specified for Native format serialization. -You must specify the property with the UserDefined serialization . +You must specify the property with the UserDefined serialization . -The maximum allowed value for this property is specified by the field. +The maximum allowed value for this property is specified by the field. The maximum size allowed is 2 gigabytes (GB). You can specify a number from 1 to 8000 bytes, or -1 to represent a value larger than 8000 bytes, up to 2 gigabytes. -For an aggregate with user-defined serialization specified, refers to the total size of the serialized data. Consider an aggregate serializing a string of 10 characters (). When the string is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized data must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. +For an aggregate with user-defined serialization specified, refers to the total size of the serialized data. Consider an aggregate serializing a string of 10 characters (). When the string is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized data must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. ]]> diff --git a/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml new file mode 100644 index 0000000000..ffd70496cc --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.xml @@ -0,0 +1,139 @@ + + + + + Used to mark a type definition in an assembly as a user-defined type (UDT) in SQL Server. The properties on the attribute reflect the physical characteristics used when the type is registered with SQL Server. This class cannot be inherited. + + custom attribute. Every UDT must be annotated with this attribute. See [CLR User-Defined Types](https://go.microsoft.com/fwlink/?LinkId=128028) for more information about UDTs, including an example of a UDT. + +## Examples +The following example shows the `UserDefinedType` attribute of the Point UDT. The UDT is byte-ordered, is named "Point", has a validation method named "ValidatePoint", and uses the native serialization format. + +[!code-csharp[SqlUserDefinedTypeAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/csharp/DataWorks_SqlUserDefinedTypeAttribute_Sample.cs#1)] +[!code-vb[SqlUserDefinedTypeAttribute Samples#1](~/../sqlclient/doc/samples/Microsoft.SqlServer.Server/visualbasic/DataWorks_SqlUserDefinedTypeAttribute_Sample.vb#1)] + +]]> + + + + One of the values representing the serialization format of the type. + A required attribute on a user-defined type (UDT), used to confirm that the given type is a UDT and to indicate the storage format of the UDT. + + + + + + The serialization format as a . + A value representing the serialization format. + + + Indicates whether the user-defined type is byte ordered. + + if the user-defined type is byte ordered; otherwise . + + property in effect guarantees that the serialized binary data can be used for semantic ordering of the information. Thus, each instance of a byte-ordered UDT object can only have one serialized representation. When a comparison operation is performed in SQL Server on the serialized bytes, its results should be the same as if the same comparison operation had taken place in managed code. + +The following features are supported when is set to `true`: + +- The ability to create indexes on columns of this type. + +- The ability to create primary and foreign keys as well as CHECK and UNIQUE constraints on columns of this type. + +- The ability to use Transact-SQL ORDER BY, GROUP BY, and PARTITION BY clauses. In these cases, the binary representation of the type is used to determine the order. + +- The ability to use comparison operators in Transact-SQL statements. + +- The ability to persist computed columns of this type. + +Note that both the `Native` and `UserDefined` serialization formats support the following comparison operators when is set to `true`: + +- Equal to (=) + +- Not equal to (!=) + +- Greater than (>) + +- Less than (\<) + +- Greater than or equal to (>=) + +- Less than or equal to (<=) + +]]> + + + + Indicates whether all instances of this user-defined type are the same length. + + if all instances of this type are the same length; otherwise . + + + . This attribute is only relevant for UDTs with `UserDefined` serialization . + +]]> + + + + The maximum size of the instance, in bytes. + An value representing the maximum size of the instance. + + property with the `UserDefined` serialization . + +When connecting to SQL Server 2005 or earlier, must be between 1 and 8000. + +When connecting to SQL Server 2008 or later, set between 1 and 8000, for a type whose instances are always 8,000 bytes or less. For types that can have instances larger than 8000, specify -1. + +For a UDT with user-defined serialization specified, refers to the total size of the UDT in its serialized form as defined by the user. Consider a UDT with a property of a string of 10 characters (). When the UDT is serialized using a , the total size of the serialized string is 22 bytes: 2 bytes per Unicode UTF-16 character, multiplied by the maximum number of characters, plus 2 control bytes of overhead incurred from serializing a binary stream. So, when determining the value of , the total size of the serialized UDT must be considered: the size of the data serialized in binary form plus the overhead incurred by serialization. + +This property should not be used with `Native` serialization . + +]]> + + + + The SQL Server name of the user-defined type. + A value representing the SQL Server name of the user-defined type. + + property is not used within SQL Server, but is used by the Microsoft Visual Studio .NET Integrated Development Environment (IDE). + +]]> + + + + The name of the method used to validate instances of the user-defined type. + A representing the name of the method used to validate instances of the user-defined type. + + + + + + diff --git a/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml b/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml new file mode 100644 index 0000000000..93f80783d0 --- /dev/null +++ b/doc/snippets/Microsoft.SqlServer.Server/SystemDataAccessKind.xml @@ -0,0 +1,26 @@ + + + + + Describes the type of access to system data for a user-defined method or function. + + and to indicate what type of access to system data the method or function has. + +Note that methods and functions are not allowed to make changes to the database, so the options for this enumeration are `None` (meaning no data-access performed by the method or function) and `Read` (meaning that the method or function performs read-only data-access operations, such as executing SELECT statements). + + ]]> + + + + The method or function does not access system data. + + + The method or function reads system data. + + + diff --git a/porting-cheat-sheet.md b/porting-cheat-sheet.md index 3258e97776..d09c16c77e 100644 --- a/porting-cheat-sheet.md +++ b/porting-cheat-sheet.md @@ -4,6 +4,20 @@ This guide is meant to cover all namespace changes needed in client applications ## Namespace Changes needed +### Microsoft.Data.SqlClient v5.0 and newer + +| Namespace Change | Applicability | +|--|--| +| `using System.Data.SqlClient;`
`using Microsoft.Data.SqlClient;` | Applicable to all classes, enums and delegates. | +| `using Microsoft.SqlServer.Server;`
`using Microsoft.Data.SqlClient.Server;` | Applicable Classes:
`SqlDataRecord`
`SqlMetaData`

1 _All remaining types continue to be referenced from Microsoft.SqlServer.Server namespace._| +| `using System.Data.SqlTypes;`
`using Microsoft.Data.SqlTypes;` | Applicable Classes:
`SqlFileStream`| +| `using System.Data.Sql;`
`using Microsoft.Data.Sql;` | Applicable Classes:
`SqlNotificationRequest`
| +| `using System.Data;`
`using Microsoft.Data;` | Applicable Classes:
`OperationAbortedException`| + +1 Breaking change for User-Defined types and Microsoft.SqlServer.Types support over _Microsoft.Data.SqlClient v3.0.0_. + +### Microsoft.Data.SqlClient v4.0 and older + | Namespace Change | Applicability | |--|--| | `using System.Data.SqlClient;`
`using Microsoft.Data.SqlClient;` | Applicable to all classes, enums and delegates. | @@ -37,6 +51,7 @@ For .NET Framework projects it may be necessary to include the following in your |--|--| | Can use DateTime object as value for SqlParameter with type `DbType.Time`. | Must use TimeSpan object as value for SqlParameter with type `DbType.Time`. | | Using DateTime object as value for SqlParameter with type `DbType.Date` would send date and time to SQL Server. | DateTime object's time components will be truncated when sent to SQL Server using `DbType.Date`. | +| `Encrypt` defaults to `false`. | Starting in v4.0, default encryption settings were made more secure, requiring opt-in to non-encrypted connections. `Encrypt` defaults to `true` and the driver will always validate the server certificate based on `TrustServerCertificate`. (Previously, server certificates would only be validated if `Encrypt` was also `true`.)

If you need to turn off encryption, you must specify `Encrypt=false`. If you use encryption with a self-signed certificate on the server, you must specify `TrustServerCertificate=true`.

In v5.0, `SqlConnectionStringBuilder.Encrypt` is no longer a `bool`. It's a `SqlConnectionEncryptOption` with multiple values to support `Strict` encryption mode (TDS 8.0). It uses implicit conversion operators to remain code-backwards compatible, but it was a binary breaking change, requiring a recompile of applications. | ## Contribute to this Cheat Sheet diff --git a/release-notes/2.1/2.1.5.md b/release-notes/2.1/2.1.5.md new file mode 100644 index 0000000000..1617db1db9 --- /dev/null +++ b/release-notes/2.1/2.1.5.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 2.1.5 released 30 August 2022 + +This update brings the below changes over the previous stable release: + +### Fixed + +- Added CommandText length validation when using stored procedure command types. [#1726](https://github.com/dotnet/SqlClient/pull/1726) +- Fixed Kerberos authentication failure when using .NET 6. [#1727](https://github.com/dotnet/SqlClient/pull/1727) +- Removed union overlay design and used reflection in `SqlTypeWorkarounds`. [#1729](https://github.com/dotnet/SqlClient/pull/1729) + +### Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Ensure connections fail when encryption is required + +In scenarios where client encryption libraries were disabled or unavailable, it was possible for unencrypted connections to be made when Encrypt was set to true or the server required encryption. + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 2.1.1 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.0 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 + +#### .NET Standard 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 2.1.1 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Runtime.Caching 4.7.0 +- Microsoft.Identity.Client 4.21.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 diff --git a/release-notes/2.1/2.1.md b/release-notes/2.1/2.1.md index e35a6f987b..2c149e041b 100644 --- a/release-notes/2.1/2.1.md +++ b/release-notes/2.1/2.1.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/08/30 | 2.1.5 | [release notes](2.1.5.md) | | 2021/09/20 | 2.1.4 | [release notes](2.1.4.md) | | 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | | 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | diff --git a/release-notes/2.1/README.md b/release-notes/2.1/README.md index e35a6f987b..2c149e041b 100644 --- a/release-notes/2.1/README.md +++ b/release-notes/2.1/README.md @@ -4,6 +4,7 @@ The following Microsoft.Data.SqlClient 2.1 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/08/30 | 2.1.5 | [release notes](2.1.5.md) | | 2021/09/20 | 2.1.4 | [release notes](2.1.4.md) | | 2021/05/21 | 2.1.3 | [release notes](2.1.3.md) | | 2021/03/03 | 2.1.2 | [release notes](2.1.2.md) | diff --git a/release-notes/3.1/3.1.0.md b/release-notes/3.1/3.1.0.md new file mode 100644 index 0000000000..eba2ad30d0 --- /dev/null +++ b/release-notes/3.1/3.1.0.md @@ -0,0 +1,75 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.1.0 released 30 March 2022 + +This update includes the following changes over the 3.0 release: + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1539](https://github.com/dotnet/SqlClient/pull/1539) [Read more](#introduce-attestation-protocol-none) +- Included `42108` and `42109` error codes to retriable transient errors list. [#1560](https://github.com/dotnet/SqlClient/pull/1560) + +### Fixed + +- Changed EnclaveDelegate.Crypto GetEnclaveProvider to use a thread safe concurrent dictionary. [#1564](https://github.com/dotnet/SqlClient/pull/1564) + +### Introduce Attestation Protocol None + +A new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.1/3.1.1.md b/release-notes/3.1/3.1.1.md new file mode 100644 index 0000000000..d7cb669712 --- /dev/null +++ b/release-notes/3.1/3.1.1.md @@ -0,0 +1,77 @@ +# Release Notes + +## Microsoft.Data.SqlClient 3.1.1 released 12 August 2022 + +This update brings the below changes over the previous release: + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1700](https://github.com/dotnet/SqlClient/pull/1700) +- Fixed Kerberos authentication failure when using .NET 6. [#1696](https://github.com/dotnet/SqlClient/pull/1696) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1695](https://github.com/dotnet/SqlClient/pull/1695) +- Removed union overlay design and use reflection in `SqlTypeWorkarounds`. [#1699](https://github.com/dotnet/SqlClient/pull/1699) + +## Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Core 2.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework 4.61 + +- Microsoft.Data.SqlClient.SNI 3.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core 2.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Core 3.1 + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Diagnostics.DiagnosticSource 4.7.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 3.0.0 +- Microsoft.Win32.Registry 4.7.0 +- System.Buffers 4.5.1 +- System.Memory 4.5.4 +- System.Security.Principal.Windows 4.7.0 +- System.Text.Encoding.CodePages 4.7.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Caching 4.7.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.14.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 5.6.0 +- Microsoft.IdentityModel.JsonWebTokens 5.6.0 +- System.Configuration.ConfigurationManager 4.7.0 +- System.Runtime.Loader 4.3.0 diff --git a/release-notes/3.1/3.1.md b/release-notes/3.1/3.1.md new file mode 100644 index 0000000000..6e4d68e534 --- /dev/null +++ b/release-notes/3.1/3.1.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 3.1 Releases + +The following Microsoft.Data.SqlClient 3.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/12 | 3.1.1 | [release notes](3.1.1.md) | +| 2022/03/30 | 3.1.0 | [release notes](3.1.0.md) | diff --git a/release-notes/3.1/README.md b/release-notes/3.1/README.md new file mode 100644 index 0000000000..6e4d68e534 --- /dev/null +++ b/release-notes/3.1/README.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 3.1 Releases + +The following Microsoft.Data.SqlClient 3.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/12 | 3.1.1 | [release notes](3.1.1.md) | +| 2022/03/30 | 3.1.0 | [release notes](3.1.0.md) | diff --git a/release-notes/4.0/4.0.1.md b/release-notes/4.0/4.0.1.md new file mode 100644 index 0000000000..7e43a9bcc8 --- /dev/null +++ b/release-notes/4.0/4.0.1.md @@ -0,0 +1,83 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.1 released 17 January 2022 + +This update brings the below changes over the previous preview release: + +### Added + +- Added AppContext switch `SuppressInsecureTLSWarning` to allow suppression of TLS security warning when using `Encrypt=false` in the connection string. [#1457](https://github.com/dotnet/SqlClient/pull/1457) [Read more](#suppress-tls-security-warnings) + +### Fixed + +- Fixed Kerberos authentication failure when using .NET 6. [#1411](https://github.com/dotnet/SqlClient/pull/1411) +- Fixed connection failure when using `SqlLocalDB` instance pipe name. [#1433](https://github.com/dotnet/SqlClient/pull/1433) +- Fixed a failure when executing concurrent queries requiring enclaves. [#1451](https://github.com/dotnet/SqlClient/pull/1451) +- Updated obsolete API calls targeting .NET 6. [#1401](https://github.com/dotnet/SqlClient/pull/1401) + +### Suppress TLS security warnings + +When connecting to a SQL Server, if a protocol lower than TLS 1.2 is negotiated, a security warning is printed out to the console. This warning can be suppressed by enabling the following `AppContext` switch on the application startup while `Encrypt` is set to `false` on connection string. + +`Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.0/4.0.2.md b/release-notes/4.0/4.0.2.md new file mode 100644 index 0000000000..96c485a613 --- /dev/null +++ b/release-notes/4.0/4.0.2.md @@ -0,0 +1,80 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.0.2 released 13 September 2022 + +This update brings the below changes over the previous preview release: + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1718](https://github.com/dotnet/SqlClient/pull/1718) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1720](https://github.com/dotnet/SqlClient/pull/1720), [#1747](https://github.com/dotnet/SqlClient/pull/1747) +- Added CommandText length validation when using stored procedure command types. [#1721](https://github.com/dotnet/SqlClient/pull/1721) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1722](https://github.com/dotnet/SqlClient/pull/1722) +- Fixed null SqlBinary as rowversion. [#1724](https://github.com/dotnet/SqlClient/pull/1724) +- Fixed table's collation overriding with default UTF8 collation. [#1750](https://github.com/dotnet/SqlClient/pull/1750) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1754](https://github.com/dotnet/SqlClient/pull/1754), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1723](https://github.com/dotnet/SqlClient/pull/1723) + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/4.0/4.0.md b/release-notes/4.0/4.0.md index 74b5e8f776..8d0ffeceb8 100644 --- a/release-notes/4.0/4.0.md +++ b/release-notes/4.0/4.0.md @@ -4,6 +4,8 @@ The following Microsoft.Data.SqlClient 4.0 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/09/13 | 4.0.2 | [release notes](4.0.2.md) | +| 2022/01/17 | 4.0.1 | [release notes](4.0.1.md) | | 2021/11/18 | 4.0.0 | [release notes](4.0.0.md) | The following Microsoft.Data.SqlClient 4.0 preview releases have been shipped: diff --git a/release-notes/4.0/README.md b/release-notes/4.0/README.md index 74b5e8f776..8d0ffeceb8 100644 --- a/release-notes/4.0/README.md +++ b/release-notes/4.0/README.md @@ -4,6 +4,8 @@ The following Microsoft.Data.SqlClient 4.0 stable releases have been shipped: | Release Date | Version | Notes | | :-- | :-- | :--: | +| 2022/09/13 | 4.0.2 | [release notes](4.0.2.md) | +| 2022/01/17 | 4.0.1 | [release notes](4.0.1.md) | | 2021/11/18 | 4.0.0 | [release notes](4.0.0.md) | The following Microsoft.Data.SqlClient 4.0 preview releases have been shipped: diff --git a/release-notes/4.1/4.1.0.md b/release-notes/4.1/4.1.0.md new file mode 100644 index 0000000000..c8fdd801da --- /dev/null +++ b/release-notes/4.1/4.1.0.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.1.0 released 31 January 2022 + +This update brings the below changes over the previous preview release: + +### Added + +- Added new Attestation Protocol `None` for `VBS` enclave types. This protocol will allow users to forgo enclave attestation for VBS enclaves. [#1419](https://github.com/dotnet/SqlClient/pull/1419) [#1425](https://github.com/dotnet/SqlClient/pull/1425) [Read more](#introduce-attestation-protocol-none) + +### Introduce Attestation Protocol None + +A new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.0 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.1/4.1.1.md b/release-notes/4.1/4.1.1.md new file mode 100644 index 0000000000..f6fbc70f19 --- /dev/null +++ b/release-notes/4.1/4.1.1.md @@ -0,0 +1,80 @@ +# Release Notes + +## Microsoft.Data.SqlClient 4.1.1 released 13 September 2022 + +This update brings the below changes over the previous preview release: + +### Fixed + +- Fixed connection failure by not requiring Certificate Revocation List (CRL) check during authentication. [#1706](https://github.com/dotnet/SqlClient/pull/1706) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1708](https://github.com/dotnet/SqlClient/pull/1708), [#1746](https://github.com/dotnet/SqlClient/pull/1746) +- Added CommandText length validation when using stored procedure command types. [#1709](https://github.com/dotnet/SqlClient/pull/1709) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1710](https://github.com/dotnet/SqlClient/pull/1710) +- Fixed null SqlBinary as rowversion. [#1712](https://github.com/dotnet/SqlClient/pull/1712) +- Fixed table's collation overriding with default UTF8 collation. [#1749](https://github.com/dotnet/SqlClient/pull/1749) + +## Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v4.0.1` [#1755](https://github.com/dotnet/SqlClient/pull/1755), which includes the fix for AppDomain crash introducing in issue [#1418](https://github.com/dotnet/SqlClient/issues/1418) +- Various code improvements: [#1711](https://github.com/dotnet/SqlClient/pull/1711) + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 4.0.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/4.1/4.1.md b/release-notes/4.1/4.1.md new file mode 100644 index 0000000000..a310caa5f7 --- /dev/null +++ b/release-notes/4.1/4.1.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 4.1 Releases + +The following Microsoft.Data.SqlClient 4.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.1.1 | [release notes](4.1.1.md) | +| 2022/01/31 | 4.1.0 | [release notes](4.1.0.md) | diff --git a/release-notes/4.1/README.md b/release-notes/4.1/README.md new file mode 100644 index 0000000000..a310caa5f7 --- /dev/null +++ b/release-notes/4.1/README.md @@ -0,0 +1,8 @@ +# Microsoft.Data.SqlClient 4.1 Releases + +The following Microsoft.Data.SqlClient 4.1 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/09/13 | 4.1.1 | [release notes](4.1.1.md) | +| 2022/01/31 | 4.1.0 | [release notes](4.1.0.md) | diff --git a/release-notes/5.0/5.0.0-preview1.md b/release-notes/5.0/5.0.0-preview1.md new file mode 100644 index 0000000000..55faad3c70 --- /dev/null +++ b/release-notes/5.0/5.0.0-preview1.md @@ -0,0 +1,136 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview1.22069.1 released 9 March 2022 + +This update brings the below changes over the previous release: + +### Added + +- Added SqlDataSourceEnumerator. [#1430](https://github.com/dotnet/SqlClient/pull/1430), [Read more](#sql-data-source-enumerator-support) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419), [Read more](#new-attestation-protocol-none) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457), [Read more](#suppress-insecure-tls-warnings) + +### Fixed + +- Fixed all documentation paths to Unix format path. [#1442](https://github.com/dotnet/SqlClient/pull/1442) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `v5.0.0-preview1.22062.1`. [#1537](https://github.com/dotnet/SqlClient/pull/1537) +- Modernized style in ValueUtilSmi. [#1351](https://github.com/dotnet/SqlClient/pull/1351) +- Changed SQL server codenames to version names. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Prevented subtype generation in project files. [#1452](https://github.com/dotnet/SqlClient/pull/1452) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Changed files in csproj to be alphabetically sorted in netfx and netcore. [#1364](https://github.com/dotnet/SqlClient/pull/1364) +- Sqlstream, SqlInternalTransaction and MetaDataUtilsSmi are moved to shared folder. [#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346) and [#1339](https://github.com/dotnet/SqlClient/pull/1339) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313),[#1330](https://github.com/dotnet/SqlClient/pull/1330),[#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435),[#1478](https://github.com/dotnet/SqlClient/pull/1478) + +### SQL Data Source Enumerator support +Provides a mechanism for enumerating all available instances of SQL Server within the local network. +```cs +using Microsoft.Data.Sql; + +static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +``` + +### New Attestation protocol `None` + new attestation protocol called `None` will be allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +### Suppress insecure TLS warnings +A security warning is ouptput on the console if the TLS version less than 1.2 is used to negotiate with the server. This warning could be suppressed on SQL connection while `Encrypt = false` by enabling the following AppContext switch on the application startup: +```cs +Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning +``` + +## Target Platform Support + +- .NET Framework 4.6.1+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0.preview1.22062.1 +- Azure.Identity 1.3.0 +- Microsoft.Identity.Client 4.22.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/5.0/5.0.0-preview2.md b/release-notes/5.0/5.0.0-preview2.md new file mode 100644 index 0000000000..16158a05fb --- /dev/null +++ b/release-notes/5.0/5.0.0-preview2.md @@ -0,0 +1,82 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview2.22096.2 released 6 April 2022 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview1 + +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + +### Fixed + +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication [#1559](https://github.com/dotnet/SqlClient/pull/1559) + +### Changed + +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0-preview2.22084.1`. [#1563](https://github.com/dotnet/SqlClient/pull/1563) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1` [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced AlwaysEncryptedAttestationException with SqlException [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to SqlParameterCollection [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Code health improvements [#1343](https://github.com/dotnet/SqlClient/pull/1343) [#1370](https://github.com/dotnet/SqlClient/pull/1370) [#1371](https://github.com/dotnet/SqlClient/pull/1371) [#1438](https://github.com/dotnet/SqlClient/pull/1438) [#1483](https://github.com/dotnet/SqlClient/pull/1483) + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encodings.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview2.22084.1 +- Azure.Identity 1.5.0 +- Microsoft.Identity.Client 4.30.1 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 \ No newline at end of file diff --git a/release-notes/5.0/5.0.0-preview3.md b/release-notes/5.0/5.0.0-preview3.md new file mode 100644 index 0000000000..19819018bd --- /dev/null +++ b/release-notes/5.0/5.0.0-preview3.md @@ -0,0 +1,128 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0-preview3 released 16 June 2022 + +This update brings the below changes over the previous release: + +### Breaking changes over preview release v5.0.0-preview2 + +- Added a dependency on the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package. This new dependency may cause namespace conflicts if your application references that namespace and still has package references (direct or indirect) to System.Data.SqlClient from .NET Core. +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) [Read more](#tds-8-enhanced-security) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) [Read more](#server-spn) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) [Read more](#support-for-aliases) + +### Fixed + +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637] (https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625] (https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500] (https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed NullReferenceException when using `SqlDependency.Start` against an Azure SQL Database.[#1294] (https://github.com/dotnet/SqlClient/pull/1294) +- Send the correct retained transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624] (https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1578] (https://github.com/dotnet/SqlClient/pull/1578) +- Adjust the default ConnectRetryCount against Azure Synapse OnDemand endpoints [#1626] (https://github.com/dotnet/SqlClient/pull/1626) + +### Changed + +- Code health improvements [#1353](https://github.com/dotnet/SqlClient/pull/1353) [#1354](https://github.com/dotnet/SqlClient/pull/1354) [#1525](https://github.com/dotnet/SqlClient/pull/1525) [#1186](https://github.com/dotnet/SqlClient/pull/1186) +- Update Azure Identity dependency from 1.5.0 to 1.6.0.[#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for SqlCommandSet [#1548] (https://github.com/dotnet/SqlClient/pull/1548) +- Rework on `TdsParserStateObjectManaged` with nullable annotations. [#1555] (https://github.com/dotnet/SqlClient/pull/1555) + +### TDS 8 Enhanced Security + +To use TDS 8, specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. TDS 8 begins and continues all server communication inside a secure, encrypted TLS connection. + +New Encrypt values have been added to clarify connection encryption behavior. Encrypt=Mandatory is equavalent to Encrypt=True and encrypts connections during the TDS connection negotiation. Encrypt=Optional is equivalent to Encrypt=False and only encrypts the connection if the server tells the client that encryption is required during the TDS connection negotiation. + +HostNameInCertificate can be specified in the connection string when using aliases to connect with encryption to a server that has a server certificate with a different name or alternate subject name than the name used by the client to identify the server (DNS aliases, for example). Example usage: HostNameInCertificate=MyDnsAliasName + +### Server SPN + +When connecting in an environment that has unique domain/forest topography, the ServerSPN/Server SPN and FailoverServerSPN/Failover Server SPN connection string settings can be used to override the auto-generated server SPNs used in the library when authenticating with integrated authentication in a domain environment. + +### Support for Aliases + +Users can configure Aliases by using the SQL Server Configuration Manager. These are stored in the Windows registry and are already supported when targeting .NET Framework. This release brings support for aliases when targeting .NET or .NET Core on Windows. + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0.0 +- Microsoft.Identity.Client 4.43.2.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0.0 +- System.Buffers 4.0.3.0 +- System.Configuration 4.0.0.0 +- System.Data 4.0.0.0 +- System.EnterpriseServices 4.0.0.0 +- System.IdentityModel.Tokens.Jwt 6.8.0.0 +- System.Runtime.Caching 4.0.0.0 +- System.Runtime.InteropServices.RuntimeInformation 4.0.2.0 +- System.Runtime.Serialization 4.0.0.0 +- System.Transactions 4.0.0.0 +- System.Xml 4.0.0.0 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.43.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0-preview3.22165.1 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.43.2 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.8.0 +- Microsoft.IdentityModel.JsonWebTokens 6.8.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Runtime.Loader 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 +- System.Security.Permissions 5.0.0 +- NetStandard.Library 2.0.3 diff --git a/release-notes/5.0/5.0.0.md b/release-notes/5.0/5.0.0.md new file mode 100644 index 0000000000..ecc981ce64 --- /dev/null +++ b/release-notes/5.0/5.0.0.md @@ -0,0 +1,196 @@ +# Release Notes + +## Microsoft.Data.SqlClient 5.0.0 released 5 August 2022 + +This update includes the following changes over the 4.1 release: + +### Breaking changes + +- As part of the [`TDS 8` feature](#tds-8-enhanced-security), the `SqlConnectionStringBuilder.Encrypt` property has changed from a `bool` to a `SqlConnectionEncryptOption`. `SqlConnectionEncryptOption` has implicit conversion rules to convert to/from a `bool` so that existing code remains backwards compatible, however this is a binary-breaking change and a recompile is required against this version. +- Added a dependency on the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package. This new dependency may cause namespace conflicts if your application references that namespace and still has package references (direct or indirect) to System.Data.SqlClient from .NET Core. +- Dropped classes from the `Microsoft.Data.SqlClient.Server` namespace and replaced them with supported types from the [Microsoft.SqlServer.Server](https://github.com/dotnet/SqlClient/tree/main/src/Microsoft.SqlServer.Server) package.[#1585](https://github.com/dotnet/SqlClient/pull/1585) The affected classes and enums are: + - Microsoft.Data.SqlClient.Server.IBinarySerialize -> Microsoft.SqlServer.Server.IBinarySerialize + - Microsoft.Data.SqlClient.Server.InvalidUdtException -> Microsoft.SqlServer.Server.InvalidUdtException + - Microsoft.Data.SqlClient.Server.SqlFacetAttribute -> Microsoft.SqlServer.Server.SqlFacetAttribute + - Microsoft.Data.SqlClient.Server.SqlFunctionAttribute -> Microsoft.SqlServer.Server.SqlFunctionAttribute + - Microsoft.Data.SqlClient.Server.SqlMethodAttribute -> Microsoft.SqlServer.Server.SqlMethodAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedAggregateAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute + - Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute -> Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute + - (enum) Microsoft.Data.SqlClient.Server.DataAccessKind -> Microsoft.SqlServer.Server.DataAccessKind + - (enum) Microsoft.Data.SqlClient.Server.Format -> Microsoft.SqlServer.Server.Format + - (enum) Microsoft.Data.SqlClient.Server.SystemDataAccessKind -> Microsoft.SqlServer.Server.SystemDataAccessKind +- Dropped support for .NET Framework 4.6.1 [#1574](https://github.com/dotnet/SqlClient/pull/1574) + + +### Added + +- Added support for `TDS 8`. To use TDS 8, users should specify Encrypt=Strict in the connection string. [#1608](https://github.com/dotnet/SqlClient/pull/1608) [Read more](#tds-8-enhanced-security) +- Added `TDS 8` version for TDSLogin. [#1657](https://github.com/dotnet/SqlClient/pull/1657) +- Added support for specifying Server SPN and Failover Server SPN on the connection. [#1607](https://github.com/dotnet/SqlClient/pull/1607) [Read more](#server-spn) +- Added support for aliases when targeting .NET Core on Windows. [#1588](https://github.com/dotnet/SqlClient/pull/1588) [Read more](#support-for-aliases) +- Added support for `SqlDataSourceEnumerator` on Windows. [#1430](https://github.com/dotnet/SqlClient/pull/1430), [Read more](#sql-data-source-enumerator-support) +- Added new attestation protocol `None` option to forgo enclave attestation when using VBS enclaves. [#1425](https://github.com/dotnet/SqlClient/pull/1425) and [#1419](https://github.com/dotnet/SqlClient/pull/1419), [Read more](#new-attestation-protocol-none) +- Added a new AppContext switch to suppress insecure TLS warnings. [#1457](https://github.com/dotnet/SqlClient/pull/1457), [Read more](#suppress-insecure-tls-warnings) + +### Fixed + +- Fixed null SqlBinary as rowversion. [#1688](https://github.com/dotnet/SqlClient/pull/1688) +- Fixed **KeyNotFoundException** for the `FailoverPartner` key on SQL servers with availability group configured. [#1614](https://github.com/dotnet/SqlClient/pull/1614) +- Fixed naming, order, and formatting for `SqlDiagnosticsListener` on .NET Core and .NET. [#1637](https://github.com/dotnet/SqlClient/pull/1637) +- Fixed NullReferenceException during Azure Active Directory authentication. [#1625](https://github.com/dotnet/SqlClient/pull/1625) +- Added CommandText length validation when using stored procedure command types. [#1484](https://github.com/dotnet/SqlClient/pull/1484) +- Fixed `GetSchema("StructuredTypeMembers")` to return correct schema information. [#1500](https://github.com/dotnet/SqlClient/pull/1500), [#1639](https://github.com/dotnet/SqlClient/pull/1639) +- Fixed **NullReferenceException** when using `SqlDependency.Start` against an Azure SQL Database. [#1294](https://github.com/dotnet/SqlClient/pull/1294) +- Fixed transaction descriptor in the MARS TDS Header when there is no current transaction on .NET 5+ and .NET Core. [#1624](https://github.com/dotnet/SqlClient/pull/1624) +- Parallelize SSRP requests on Linux and macOS when MultiSubNetFailover is specified. [#1578](https://github.com/dotnet/SqlClient/pull/1578) +- Fixed connection failure by skipping Certificate Revocation List (CRL) check during authentication. [#1559](https://github.com/dotnet/SqlClient/pull/1559) +- Fixed thread safety issue for `GetEnclaveProvider` by converting dictionary to concurrent dictionary. [#1451](https://github.com/dotnet/SqlClient/pull/1451) + +### Changed + +- Updated `AuthProviderInfo` struct to be matched the changes in native SNI for `TDS 8` server certificate validation. [#1680](https://github.com/dotnet/SqlClient/pull/1680) +- Updated default system protocol for `TDS 8` on managed code. [#1678](https://github.com/dotnet/SqlClient/pull/1678) +- Updated `Microsoft.Data.SqlClient.SNI` (.NET Framework dependency) and `Microsoft.Data.SqlClient.SNI.runtime` (.NET Core/Standard dependency) version to `5.0.0`. [#1608](https://github.com/dotnet/SqlClient/pull/1608) +- Changed encoding UTF-7 to ASCII for SSRP Broadcast. [#1671](https://github.com/dotnet/SqlClient/pull/1671) +- Updated `IdentityModel` dependency from 6.8.0 to 6.21.0 and `IdentityClient` from 4.32.2 to 4.45.0. [#1646](https://github.com/dotnet/SqlClient/pull/1646) +- Updated **Azure Identity** dependency from 1.5.0 to 1.6.0. [#1611](https://github.com/dotnet/SqlClient/pull/1611) +- Improved Regex for `SqlCommandSet`. [#1548](https://github.com/dotnet/SqlClient/pull/1548) +- Adjust the default **ConnectRetryCount** against Azure Synapse OnDemand endpoints. [#1626](https://github.com/dotnet/SqlClient/pull/1626) +- Updated `Azure.Identity` version to `1.5.0` and `Microsoft.Identity.Client` version to `4.30.1`. [#1462](https://github.com/dotnet/SqlClient/pull/1462) +- Replaced `AlwaysEncryptedAttestationException` with `SqlException`. [#1515](https://github.com/dotnet/SqlClient/pull/1515) +- Improved error message when adding wrong type to `SqlParameterCollection`. [#1547](https://github.com/dotnet/SqlClient/pull/1547) +- Changed SQL server codenames to version names in the code. [#1439](https://github.com/dotnet/SqlClient/pull/1439) +- Changed `Array.Copy` to `Buffer.BlockCopy` for byte arrays. [#1366](https://github.com/dotnet/SqlClient/pull/1366) +- Various code improvements: [#1197](https://github.com/dotnet/SqlClient/pull/1197), [#1313](https://github.com/dotnet/SqlClient/pull/1313), [#1330](https://github.com/dotnet/SqlClient/pull/1330), [#1366](https://github.com/dotnet/SqlClient/pull/1366), [#1435](https://github.com/dotnet/SqlClient/pull/1435), [#1478](https://github.com/dotnet/SqlClient/pull/1478), [#1353](https://github.com/dotnet/SqlClient/pull/1353), [#1354](https://github.com/dotnet/SqlClient/pull/1354), [#1525](https://github.com/dotnet/SqlClient/pull/1525), [#1186](https://github.com/dotnet/SqlClient/pull/1186), [#1343](https://github.com/dotnet/SqlClient/pull/1343), [#1370](https://github.com/dotnet/SqlClient/pull/1370), [#1371](https://github.com/dotnet/SqlClient/pull/1371), [#1438](https://github.com/dotnet/SqlClient/pull/1438), [#1483](https://github.com/dotnet/SqlClient/pull/1483), [#1351](https://github.com/dotnet/SqlClient/pull/1351), [#1452](https://github.com/dotnet/SqlClient/pull/1452), [#1364](https://github.com/dotnet/SqlClient/pull/1364),[#1337](https://github.com/dotnet/SqlClient/pull/1337), [#1346](https://github.com/dotnet/SqlClient/pull/1346), [#1339](https://github.com/dotnet/SqlClient/pull/1339), [#1555](https://github.com/dotnet/SqlClient/pull/1555) + + +### TDS 8 Enhanced Security + +To use TDS 8.0, specify Encrypt=Strict in the connection string. Strict mode disables TrustServerCertificate (always treated as False in Strict mode). HostNameInCertificate has been added to help some Strict mode scenarios. TDS 8 begins and continues all server communication inside a secure, encrypted TLS connection. + +New Encrypt values have been added to clarify connection encryption behavior. Encrypt=Mandatory is equivalent to Encrypt=True and encrypts connections during the TDS connection negotiation. Encrypt=Optional is equivalent to Encrypt=False and only encrypts the connection if the server tells the client that encryption is required during the TDS connection negotiation. + +HostNameInCertificate can be specified in the connection string when using aliases to connect with encryption to a server that has a server certificate with a different name or alternate subject name than the name used by the client to identify the server (DNS aliases, for example). Example usage: HostNameInCertificate=MyDnsAliasName + +To read more about TDS 8.0 in SQL Server, see the [SQL Server online documentation](https://docs.microsoft.com/sql/relational-databases/security/networking/tds-8-and-tls-1-3). + +### Server SPN + +When connecting in an environment that has unique domain/forest topography, the ServerSPN/Server SPN and FailoverServerSPN/Failover Server SPN connection string settings can be used to override the auto-generated server SPNs used in the library when authenticating with integrated authentication in a domain environment. + +### Support for Aliases + +Users can configure Aliases by using the SQL Server Configuration Manager. These are stored in the Windows registry and are already supported when targeting .NET Framework. This release brings support for aliases when targeting .NET or .NET Core on Windows. + + +### SQL Data Source Enumerator support +Provides a mechanism for enumerating all available instances of SQL Server within the local network. +```cs +using Microsoft.Data.Sql; + +static void Main() + { + // Retrieve the enumerator instance and then the data. + SqlDataSourceEnumerator instance = + SqlDataSourceEnumerator.Instance; + System.Data.DataTable table = instance.GetDataSources(); + + // Display the contents of the table. + DisplayData(table); + + Console.WriteLine("Press any key to continue."); + Console.ReadKey(); + } + + private static void DisplayData(System.Data.DataTable table) + { + foreach (System.Data.DataRow row in table.Rows) + { + foreach (System.Data.DataColumn col in table.Columns) + { + Console.WriteLine("{0} = {1}", col.ColumnName, row[col]); + } + Console.WriteLine("============================"); + } + } +``` + +### New Attestation protocol `None` +A new attestation protocol called `None` is allowed in the connection string. This protocol will allow users to forgo enclave attestation for `VBS` enclaves. When this protocol is set, the enclave attestation URL property is optional. + +Connection string example: + +```cs +//Attestation protocol NONE with no URL +"Data Source = {server}; Initial Catalog = {db}; Column Encryption Setting = Enabled; Attestation Protocol = None;" + +``` + +### Suppress insecure TLS warnings +A security warning is output to the console if a TLS version less than 1.2 is used to negotiate encryption with the server. This warning can be suppressed on connections where `Encrypt = false` by enabling the following AppContext switch at application startup: +```cs +Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning +``` + +## Target Platform Support + +- .NET Framework 4.6.2+ (Windows x86, Windows x64) +- .NET Core 3.1+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) +- .NET Standard 2.0+ (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) + +### Dependencies + +#### .NET Framework + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.InteropServices.RuntimeInformation 4.3.0 +- System.Security.Cryptography.Algorithms 4.3.1 +- System.Security.Cryptography.Primitives 4.3.0 +- System.Text.Encoding.Web 4.7.2 + +#### .NET Core + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.Diagnostics.DiagnosticSource 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 + +#### .NET Standard + +- Microsoft.Data.SqlClient.SNI.runtime 5.0.0 +- Azure.Identity 1.6.0 +- Microsoft.Identity.Client 4.45.0 +- Microsoft.IdentityModel.Protocols.OpenIdConnect 6.21.0 +- Microsoft.IdentityModel.JsonWebTokens 6.21.0 +- Microsoft.SqlServer.Server 1.0.0 +- Microsoft.Win32.Registry 5.0.0 +- System.Buffers 4.5.1 +- System.Configuration.ConfigurationManager 5.0.0 +- System.IO 4.3.0 +- System.Runtime.Caching 5.0.0 +- System.Text.Encoding.CodePages 5.0.0 +- System.Text.Encodings.Web 4.7.2 +- System.Runtime.Loader 4.3.0 +- System.Resources.ResourceManager 4.3.0 +- System.Security.Cryptography.Cng 5.0.0 +- System.Security.Principal.Windows 5.0.0 diff --git a/release-notes/5.0/5.0.md b/release-notes/5.0/5.0.md new file mode 100644 index 0000000000..6fa437317c --- /dev/null +++ b/release-notes/5.0/5.0.md @@ -0,0 +1,15 @@ +# Microsoft.Data.SqlClient 5.0 Releases + +The following Microsoft.Data.SqlClient 5.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/05 | 5.0.0 | [release notes](5.0.0.md) | + +The following Microsoft.Data.SqlClient 5.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/06/16 | 5.0.0-preview3.22168.1 | [release notes](5.0.0-preview3.md) | +| 2022/04/06 | 5.0.0-preview2.22096.2 | [release notes](5.0.0-preview2.md) | +| 2022/03/09 | 5.0.0-preview1.22069.1 | [release notes](5.0.0-preview1.md) | diff --git a/release-notes/5.0/README.md b/release-notes/5.0/README.md new file mode 100644 index 0000000000..6fa437317c --- /dev/null +++ b/release-notes/5.0/README.md @@ -0,0 +1,15 @@ +# Microsoft.Data.SqlClient 5.0 Releases + +The following Microsoft.Data.SqlClient 5.0 stable releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/08/05 | 5.0.0 | [release notes](5.0.0.md) | + +The following Microsoft.Data.SqlClient 5.0 preview releases have been shipped: + +| Release Date | Version | Notes | +| :-- | :-- | :--: | +| 2022/06/16 | 5.0.0-preview3.22168.1 | [release notes](5.0.0-preview3.md) | +| 2022/04/06 | 5.0.0-preview2.22096.2 | [release notes](5.0.0-preview2.md) | +| 2022/03/09 | 5.0.0-preview1.22069.1 | [release notes](5.0.0-preview1.md) | diff --git a/release-notes/MSqlServerServer/1.0/1.0.0.md b/release-notes/MSqlServerServer/1.0/1.0.0.md new file mode 100644 index 0000000000..72072f174a --- /dev/null +++ b/release-notes/MSqlServerServer/1.0/1.0.0.md @@ -0,0 +1,42 @@ +# Release Notes + +## General Availability of Microsoft.SqlServer.Server + +_**1.0.0 released 28 January 2022**_ + +This is the initial public stable release of the **Microsoft.SqlServer.Server** namespace in a separate assembly. This library is a dependency for **Microsoft.Data.SqlClient** enabling cross framework support of UDT types. + +### Supported types + +#### Targeting .NET Standard 2.0 + +- Microsoft.SqlServer.Server.DataAccessKind +- Microsoft.SqlServer.Server.Format +- Microsoft.SqlServer.Server.IBinarySerialize +- Microsoft.SqlServer.Server.InvalidUdtException +- Microsoft.SqlServer.Server.SqlFacetAttribute +- Microsoft.SqlServer.Server.SqlFunctionAttribute +- Microsoft.SqlServer.Server.SqlMethodAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute +- Microsoft.SqlServer.Server.SystemDataAccessKind + +#### Targeting .NET Framework 4.6+ + +Following types forwarded to `System.Data`: + +- Microsoft.SqlServer.Server.DataAccessKind +- Microsoft.SqlServer.Server.Format +- Microsoft.SqlServer.Server.IBinarySerialize +- Microsoft.SqlServer.Server.InvalidUdtException +- Microsoft.SqlServer.Server.SqlFacetAttribute +- Microsoft.SqlServer.Server.SqlFunctionAttribute +- Microsoft.SqlServer.Server.SqlMethodAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute +- Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute +- Microsoft.SqlServer.Server.SystemDataAccessKind + +## Target Platform Support + +- .NET Framework 4.6+ (Windows x86, Windows x64) +- .NET Standard 2.0 (Windows x86, Windows x64, Windows ARM64, Windows ARM, Linux, macOS) diff --git a/release-notes/MSqlServerServer/1.0/README.md b/release-notes/MSqlServerServer/1.0/README.md new file mode 100644 index 0000000000..ffe4befce8 --- /dev/null +++ b/release-notes/MSqlServerServer/1.0/README.md @@ -0,0 +1,7 @@ +# Microsoft.SqlServer.Server 1.0 Releases + +The following Microsoft.SqlServer.Server 1.0 stable release has been shipped: + +| Release Date | Description | Notes | +| :-- | :-- | :--: | +| 2022/01/28 | 1.0.0 | [release notes](1.0.0.md) | diff --git a/release-notes/README.md b/release-notes/README.md index ae992b42c4..cff1b6b88d 100644 --- a/release-notes/README.md +++ b/release-notes/README.md @@ -1,11 +1,14 @@ # Microsoft.Data.SqlClient Release Notes -The latest stable release is [Microsoft.Data.SqlClient 4.0](4.0). +The latest stable release is [Microsoft.Data.SqlClient 5.0](5.0). ## Release Information +- [Microsoft.Data.SqlClient 5.0](5.0) +- [Microsoft.Data.SqlClient 4.1](4.1) - [Microsoft.Data.SqlClient 4.0](4.0) - [Microsoft.Data.SqlClient 3.0](3.0) +- [Microsoft.Data.SqlClient 3.1](3.1) - [Microsoft.Data.SqlClient 2.1](2.1) - [Microsoft.Data.SqlClient 2.0](2.0) - [Microsoft.Data.SqlClient 1.1](1.1) @@ -22,3 +25,11 @@ The latest stable release is [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyV - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.2](add-ons/AzureKeyVaultProvider/1.2) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.1](add-ons/AzureKeyVaultProvider/1.1) - [Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider 1.0](add-ons/AzureKeyVaultProvider/1.0) + +# Microsoft.SqlServer.Server Release Notes + +The latest stable release is [Microsoft.SqlServer.Server 1.0](MSqlServerServer/1.0). + +## Release Information + +- [Microsoft.SqlServer.Server 1.0](MSqlServerServer/1.0) diff --git a/roadmap.md b/roadmap.md index 0348108333..4771802b4f 100644 --- a/roadmap.md +++ b/roadmap.md @@ -13,8 +13,10 @@ The Microsoft.Data.SqlClient roadmap communicates project priorities for evolvin |---------------------------|--------------|---------------| | Microsoft.Data.SqlClient v1.1 (servicing) | As needed (see also [1.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/1.1)) | Closed | | Microsoft.Data.SqlClient v2.1 (servicing) | As needed (see also [2.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/2.1)) | Closed | -| Microsoft.Data.SqlClient v3.0 (servicing) | As needed (see also [3.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/3.0) | Closed | -| Microsoft.Data.SqlClient v4.0 | GA (General Availability) estimated for November 2021 | [SqlClient 4.0.0](https://github.com/dotnet/SqlClient/projects/8) +| Microsoft.Data.SqlClient v3.0 (servicing) | As needed (see also [3.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/3.0)) | Closed | +| Microsoft.Data.SqlClient v4.0 (servicing) | As needed (see also [4.0 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/4.0)) | Closed | +| Microsoft.Data.SqlClient v4.1 (servicing) | As needed (see also [4.1 releases](https://github.com/dotnet/sqlclient/blob/master/release-notes/4.1)) | Closed | +| Microsoft.Data.SqlClient v5.0 | GA (General Availability) estimated for May 2022 | [SqlClient 5.0.0](https://github.com/dotnet/SqlClient/projects/9) > Note: Dates are calendar year (as opposed to fiscal year). diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 4f15f38412..afcf503118 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -41,10 +41,12 @@ $(RepoRoot)artifacts\$(ReferenceType)\ $(Artifacts)tools\ $(ProjectDir)Microsoft.Data.SqlClient\ + $(ProjectDir)Microsoft.SqlServer.Server\ $(ManagedSourceCode)netcore\ $(ManagedSourceCode)netfx\ $(ManagedSourceCode)netfx\src\Resources\ $(ManagedSourceCode)add-ons\ + $(RepoRoot)src\Microsoft.SqlServer.Server\ $(Artifacts)obj\ $(NetCoreSource)src\Common\src $(NetCoreSource)src\Common\tests diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln index e3a765ce11..c5b34945a0 100644 --- a/src/Microsoft.Data.SqlClient.sln +++ b/src/Microsoft.Data.SqlClient.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29521.150 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Data.SqlClient", "Microsoft.Data.SqlClient\netfx\src\Microsoft.Data.SqlClient.csproj", "{407890AC-9876-4FEF-A6F1-F36A876BAADE}" EndProject @@ -120,6 +120,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlClient", ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnection.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionAttestationProtocol.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionColumnEncryptionSetting.xml + ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionEncryptOption.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlConnectionStringBuilder.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlCredential.xml ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml = ..\doc\snippets\Microsoft.Data.SqlClient\SqlDataAdapter.xml @@ -200,6 +201,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.Do EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Data.SqlClient.PerformanceTests", "Microsoft.Data.SqlClient\tests\PerformanceTests\Microsoft.Data.SqlClient.PerformanceTests.csproj", "{599A336B-2A5F-473D-8442-1223ED37C93E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4F3CD363-B1E6-4D6D-9466-97D78A56BE45}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlServer.Server", "Microsoft.SqlServer.Server\Microsoft.SqlServer.Server.csproj", "{A314812A-7820-4565-A2A8-ABBE391C11E4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -472,6 +477,18 @@ Global {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x64.Build.0 = Release|x64 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.ActiveCfg = Release|x86 {599A336B-2A5F-473D-8442-1223ED37C93E}.Release|x86.Build.0 = Release|x86 + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x64.ActiveCfg = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x64.Build.0 = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x86.ActiveCfg = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Debug|x86.Build.0 = Debug|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|Any CPU.Build.0 = Release|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x64.ActiveCfg = Release|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x64.Build.0 = Release|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.ActiveCfg = Release|Any CPU + {A314812A-7820-4565-A2A8-ABBE391C11E4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -507,6 +524,7 @@ Global {B499E477-C9B1-4087-A5CF-5C762D90E433} = {0CC4817A-12F3-4357-912C-09315FAAD008} {B93A3149-67E8-491E-A1E5-19D65F9D9E98} = {0CC4817A-12F3-4357-912C-09315FAAD008} {599A336B-2A5F-473D-8442-1223ED37C93E} = {0CC4817A-12F3-4357-912C-09315FAAD008} + {A314812A-7820-4565-A2A8-ABBE391C11E4} = {4F3CD363-B1E6-4D6D-9466-97D78A56BE45} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {01D48116-37A2-4D33-B9EC-94793C702431} diff --git a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props index 31b05b7660..d2d8fc7400 100644 --- a/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props +++ b/src/Microsoft.Data.SqlClient/add-ons/Directory.Build.props @@ -16,7 +16,7 @@ - net461 + net462 netstandard2.0 netcoreapp3.1 @@ -37,7 +37,7 @@ netstandard2.0;netstandard2.1 netcoreapp3.1 - net461 + net462 diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 22cbb29f6e..db0842d4f2 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -29,6 +29,15 @@ public SqlNotificationRequest(string userData, string options, int timeout) { } /// public string UserData { get { throw null; } set { } } } + + /// + public sealed class SqlDataSourceEnumerator : System.Data.Common.DbDataSourceEnumerator + { + /// + public static SqlDataSourceEnumerator Instance { get; } + /// + public override System.Data.DataTable GetDataSources() { throw null; } + } } namespace Microsoft.Data.SqlTypes { @@ -374,6 +383,7 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { } /// public new void RemoveAt(int index) { } } + /// [System.FlagsAttribute] public enum SqlBulkCopyOptions @@ -461,6 +471,8 @@ public static partial class SqlClientMetaDataCollectionNames public static readonly string AllColumns; /// public static readonly string ColumnSetColumns; + /// + public static readonly string StructuredTypeMembers; } #if NETCOREAPP || NETSTANDARD21_AND_ABOVE /// @@ -472,10 +484,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 @@ -493,6 +503,37 @@ public enum SqlConnectionIPAddressPreference /// UsePlatformDefault = 2 } + /// + public sealed class SqlConnectionEncryptOption + { + /// + public static SqlConnectionEncryptOption Parse(string value) => throw null; + /// + public static bool TryParse(string value, out SqlConnectionEncryptOption result) => throw null; + /// + public static SqlConnectionEncryptOption Optional => throw null; + + /// + public static SqlConnectionEncryptOption Mandatory => throw null; + + /// + public static SqlConnectionEncryptOption Strict => throw null; + + /// + public static implicit operator SqlConnectionEncryptOption(bool value) => throw null; + + /// + public static implicit operator bool(SqlConnectionEncryptOption value) => throw null; + + /// + public override string ToString() { throw null; } + + /// + public override bool Equals(object obj) { throw null; } + + /// + public override int GetHashCode() { throw null; } + } /// public partial class SqlColumnEncryptionCertificateStoreProvider : Microsoft.Data.SqlClient.SqlColumnEncryptionKeyStoreProvider { @@ -987,7 +1028,11 @@ public SqlConnectionStringBuilder(string connectionString) { } /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] - public bool Encrypt { get { throw null; } set { } } + public SqlConnectionEncryptOption Encrypt { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Host Name In Certificate")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string HostNameInCertificate { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Enlist")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -996,6 +1041,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Failover Partner")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string FailoverPartner { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Failover Partner SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string FailoverPartnerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Initial Catalog")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -1054,6 +1103,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Replication")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool Replication { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Server SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string ServerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Transaction Binding")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -1823,37 +1876,6 @@ public sealed class SqlConfigurableRetryFactory } namespace Microsoft.Data.SqlClient.Server { - /// - public enum DataAccessKind - { - /// - None = 0, - /// - Read = 1 - } - /// - public enum Format - { - /// - Unknown = 0, - /// - Native = 1, - /// - UserDefined = 2 - } - /// - public interface IBinarySerialize - { - /// - void Read(System.IO.BinaryReader r); - /// - void Write(System.IO.BinaryWriter w); - } - /// - public sealed partial class InvalidUdtException : System.SystemException - { - internal InvalidUdtException() { } - } /// public partial class SqlDataRecord : System.Data.IDataRecord { @@ -2024,44 +2046,6 @@ public virtual void SetValue(int ordinal, object value) { } /// public virtual int SetValues(params object[] values) { throw null; } } - /// - [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue | System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public partial class SqlFacetAttribute : System.Attribute - { - /// - public SqlFacetAttribute() { } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsNullable { get { throw null; } set { } } - /// - public int MaxSize { get { throw null; } set { } } - /// - public int Precision { get { throw null; } set { } } - /// - public int Scale { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public partial class SqlFunctionAttribute : System.Attribute - { - /// - public SqlFunctionAttribute() { } - /// - public bool IsDeterministic { get { throw null; } set { } } - /// - public DataAccessKind DataAccess { get { throw null; } set { } } - /// - public SystemDataAccessKind SystemDataAccess { get { throw null; } set { } } - /// - public bool IsPrecise { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - /// - public string TableDefinition { get { throw null; } set { } } - /// - public string FillRowMethodName { get { throw null; } set { } } - } /// public sealed partial class SqlMetaData { @@ -2200,69 +2184,6 @@ public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDe /// public static Microsoft.Data.SqlClient.Server.SqlMetaData InferFromValue(object value, string name) { throw null; } } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public sealed partial class SqlMethodAttribute : SqlFunctionAttribute - { - /// - public SqlMethodAttribute() { } - /// - public bool OnNullCall { get { throw null; } set { } } - /// - public bool IsMutator { get { throw null; } set { } } - /// - public bool InvokeIfReceiverIsNull { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] - public sealed partial class SqlUserDefinedAggregateAttribute : System.Attribute - { - /// - public const int MaxByteSizeValue = 8000; - /// - public SqlUserDefinedAggregateAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsInvariantToDuplicates { get { throw null; } set { } } - /// - public bool IsInvariantToNulls { get { throw null; } set { } } - /// - public bool IsInvariantToOrder { get { throw null; } set { } } - /// - public bool IsNullIfEmpty { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string Name { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] - public sealed partial class SqlUserDefinedTypeAttribute : System.Attribute - { - /// - public SqlUserDefinedTypeAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsByteOrdered { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string ValidationMethodName { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - } - /// - public enum SystemDataAccessKind - { - /// - None = 0, - /// - Read = 1 - } } namespace Microsoft.Data.SqlClient.DataClassification { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs index 76e50f6a1d..5b908a3b8e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/System/Net/Logging/NetEventSource.Common.cs @@ -13,7 +13,7 @@ using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -#if NET461 +#if net462 using System.Security; #endif @@ -45,7 +45,7 @@ namespace System.Net // method that takes an object and optionally provides a string representation of it, in case a particular library wants to customize further. /// Provides logging facilities for System.Net libraries. -#if NET461 +#if net462 [SecuritySafeCritical] #endif internal sealed partial class NetEventSource : EventSource diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs index ed33b6897a..0b09ea3341 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Common.cs @@ -3,11 +3,15 @@ // See the LICENSE file in the project root for more information. using Microsoft.Data.SqlClient.SNI; +using System; +using System.Runtime.InteropServices; namespace Microsoft.Data.SqlClient { internal static partial class SNINativeMethodWrapper { + private const string SNI = "Microsoft.Data.SqlClient.SNI.dll"; + internal enum SniSpecialErrors : uint { LocalDBErrorCode = SNICommon.LocalDBErrorCode, diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs index 7f1b3e17ea..eae47ef2f6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs @@ -2,18 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Data.Common; -using Microsoft.Data.SqlClient; using System; using System.Runtime.InteropServices; using System.Text; +using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; namespace Microsoft.Data.SqlClient { internal static partial class SNINativeMethodWrapper { - private const string SNI = "Microsoft.Data.SqlClient.SNI.dll"; - private static int s_sniMaxComposedSpnLength = -1; private const int SniOpenTimeOut = -1; // infinite @@ -21,6 +19,8 @@ internal static partial class SNINativeMethodWrapper [UnmanagedFunctionPointer(CallingConvention.StdCall)] internal delegate void SqlAsyncCallbackDelegate(IntPtr m_ConsKey, IntPtr pPacket, uint dwError); + internal delegate IntPtr SqlClientCertificateDelegate(IntPtr pCallbackContext); + internal const int SniIP6AddrStringBufferLength = 48; // from SNI layer internal static int SniMaxComposedSpnLength @@ -45,6 +45,23 @@ internal struct ConsumerInfo internal IntPtr key; } + [StructLayout(LayoutKind.Sequential)] + internal struct AuthProviderInfo + { + public uint flags; + [MarshalAs(UnmanagedType.Bool)] + public bool tlsFirst; + [MarshalAs(UnmanagedType.LPWStr)] + public string certId; + [MarshalAs(UnmanagedType.Bool)] + public bool certHash; + public object clientCertificateCallbackContext; + public SqlClientCertificateDelegate clientCertificateCallback; + [MarshalAs(UnmanagedType.LPWStr)] + public string serverCertFileName; + }; + + internal enum ConsumerNumber { SNI_Consumer_SNI, @@ -150,6 +167,8 @@ private unsafe struct SNI_CLIENT_CONSUMER_INFO public Sni_Consumer_Info ConsumerInfo; [MarshalAs(UnmanagedType.LPWStr)] public string wszConnectionString; + [MarshalAs(UnmanagedType.LPWStr)] + public string HostNameInCertificate; public PrefixEnum networkLibrary; public byte* szSPN; public uint cchSPN; @@ -200,9 +219,13 @@ internal struct SNI_Error #endregion #region DLL Imports + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIAddProviderWrapper")] internal static extern uint SNIAddProvider(SNIHandle pConn, ProviderEnum ProvNum, [In] ref uint pInfo); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIAddProviderWrapper")] + internal static extern uint SNIAddProvider(SNIHandle pConn, ProviderEnum ProvNum, [In] ref AuthProviderInfo pInfo); + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNICheckConnectionWrapper")] internal static extern uint SNICheckConnection([In] SNIHandle pConn); @@ -306,18 +329,30 @@ private static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] private static extern uint SNIWriteSyncOverAsync(SNIHandle pConn, [In] SNIPacket pPacket); - #endregion + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumOpenWrapper")] + internal static extern IntPtr SNIServerEnumOpen(); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] + internal static extern void SNIServerEnumClose([In] IntPtr packet); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); + #endregion internal static uint SniGetConnectionId(SNIHandle pConn, ref Guid connId) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_CONNID, out connId); } - + internal static uint SniGetProviderNumber(SNIHandle pConn, ref ProviderEnum provNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PROVIDERNUM, out provNum); } - + internal static uint SniGetConnectionPort(SNIHandle pConn, ref ushort portNum) { return SNIGetInfoWrapper(pConn, QTypes.SNI_QUERY_CONN_PEERPORT, out portNum); @@ -358,9 +393,21 @@ internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHan return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ipPreference, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, - bool fSync, int timeout, bool fParallel, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenSyncEx( + ConsumerInfo consumerInfo, + string constring, + ref IntPtr pConn, + byte[] spnBuffer, + byte[] instanceName, + bool fOverrideCache, + bool fSync, + int timeout, + bool fParallel, + SqlConnectionIPAddressPreference ipPreference, + SQLDNSInfo cachedDNSInfo, + string hostNameInCertificate) { + fixed (byte* pin_instanceName = &instanceName[0]) { SNI_CLIENT_CONSUMER_INFO clientConsumerInfo = new SNI_CLIENT_CONSUMER_INFO(); @@ -369,8 +416,8 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons MarshalConsumerInfo(consumerInfo, ref clientConsumerInfo.ConsumerInfo); clientConsumerInfo.wszConnectionString = constring; + clientConsumerInfo.HostNameInCertificate = hostNameInCertificate; clientConsumerInfo.networkLibrary = PrefixEnum.UNKNOWN_PREFIX; - clientConsumerInfo.szInstanceName = pin_instanceName; clientConsumerInfo.cchInstanceName = (uint)instanceName.Length; clientConsumerInfo.fOverrideLastConnectCache = fOverrideCache; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index d905ad16a9..5b72e28c18 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -25,62 +25,59 @@ $(DefineConstants);NETSTANDARD; - - $(DefineConstants);NETCOREAPP31_AND_ABOVE - portable true - - Microsoft\Data\SqlClient\SqlClientEventSource.cs - - - Microsoft\Data\SqlClient\SqlClientLogger.cs - - - Microsoft\Data\Sql\SqlNotificationRequest.cs - Microsoft\Data\Common\ActivityCorrelator.cs - - Microsoft\Data\Common\DbConnectionStringCommon.cs + + Microsoft\Data\Common\AdapterUtil.cs + + + Microsoft\Data\Common\DbConnectionOptions.Common.cs Microsoft\Data\Common\DbConnectionPoolKey.cs + + Microsoft\Data\Common\DbConnectionStringCommon.cs + Microsoft\Data\Common\MultipartIdentifier.cs Microsoft\Data\Common\NameValuePair.cs - - Microsoft\Data\Common\DbConnectionOptions.Common.cs + + Microsoft\Data\DataException.cs - - Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs + + Microsoft\Data\OperationAbortedException.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs - Common\Microsoft\Data\ProviderBase\DbMetaDataFactory.cs + Microsoft\Data\ProviderBase\DbMetaDataFactory.cs Microsoft\Data\ProviderBase\FieldNameLookup.cs @@ -88,6 +85,21 @@ Microsoft\Data\ProviderBase\TimeoutTimer.cs + + Microsoft\Data\Sql\SqlDataSourceEnumerator.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorManagedHelper.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorUtil.cs + + + Microsoft\Data\Sql\SqlNotificationRequest.cs + + + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs @@ -97,18 +109,75 @@ Microsoft\Data\SqlClient\AssemblyRef.cs - - Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationProvider.cs + + Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs + + + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.cs + + + Microsoft\Data\SqlClient\EnclavePackage.cs Microsoft\Data\SqlClient\LocalAppContextSwitches.cs + + Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + + Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs + + + Microsoft\Data\SqlClient\PoolBlockingPeriod.cs + + + Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalBaseEnumerator.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogic.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBase.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBaseProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryFactory.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalEnumerators.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + + + Microsoft\Data\SqlClient\SqlSequentialTextReader.cs + Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs - - Microsoft\Data\SqlClient\Server\IBinarySerialize.cs - Microsoft\Data\SqlClient\Server\ITypedGetters.cs @@ -124,38 +193,38 @@ Microsoft\Data\SqlClient\Server\MemoryRecordBuffer.cs - - Microsoft\Data\SqlClient\Server\SmiGettersStream.cs + + Microsoft\Data\SqlClient\Server\MetadataUtilsSmi.cs - - Microsoft\Data\SqlClient\Server\SmiSettersStream.cs + + Microsoft\Data\SqlClient\Server\SmiEventSink.cs - - Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs + + Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs - - Microsoft\Data\SqlClient\Server\SqlFacetAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiGettersStream.cs - - Microsoft\Data\SqlClient\Server\SqlFunctionAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiMetaData.cs - - Microsoft\Data\SqlClient\Server\SqlMetaData.cs + + Microsoft\Data\SqlClient\Server\SmiMetaDataProperty.cs - - Microsoft\Data\SqlClient\Server\SqlMethodAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs - - Microsoft\Data\SqlClient\Server\SqlNormalizer.cs + + Microsoft\Data\SqlClient\Server\SmiSettersStream.cs - - Microsoft\Data\SqlClient\Server\SqlUserDefinedTypeAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiTypedGetterSetter.cs - - Microsoft\Data\SqlClient\Server\SqlUserDefinedAggregateAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiXetterAccessMap.Common.cs - - Microsoft\Data\SqlClient\Server\SmiMetaData.cs + + Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs Microsoft\Data\SqlClient\Server\SqlDataRecord.cs @@ -163,36 +232,27 @@ Microsoft\Data\SqlClient\Server\SqlDataRecord.netcore.cs - - Microsoft\Data\SqlClient\Server\SmiXetterAccessMap.Common.cs - - - Microsoft\Data\SqlClient\Server\SmiMetaDataProperty.cs - - - Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs + + Microsoft\Data\SqlClient\Server\SqlMetaData.cs - - Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + Microsoft\Data\SqlClient\Server\SqlNormalizer.cs - - Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs + + Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs - - Microsoft\Data\SqlClient\PoolBlockingPeriod.cs + + Microsoft\Data\SqlClient\SqlTransaction.Common.cs - - Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs + + Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs - - Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + + Microsoft\Data\SqlClient\SignatureVerificationCache.cs Microsoft\Data\SqlClient\SortOrder.cs - - Microsoft\Data\SqlClient\SqlAuthenticationToken.cs - Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Algorithm.cs @@ -208,21 +268,27 @@ Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs + + Microsoft\Data\SqlClient\SqlBuffer.cs + + + Microsoft\Data\SqlClient\SqlAuthenticationToken.cs + Microsoft\Data\SqlClient\SqlBulkCopyColumnMapping.cs Microsoft\Data\SqlClient\SqlBulkCopyColumnMappingCollection.cs - - Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs - Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHint.cs - + Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHintCollection.cs + + Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + Microsoft\Data\SqlClient\SqlCachedBuffer.cs @@ -238,6 +304,12 @@ Microsoft\Data\SqlClient\SqlClientEncryptionType.cs + + Microsoft\Data\SqlClient\SqlClientEventSource.cs + + + Microsoft\Data\SqlClient\SqlClientLogger.cs + Microsoft\Data\SqlClient\SqlClientMetaDataCollectionNames.cs @@ -256,6 +328,9 @@ Microsoft\Data\SqlClient\SqlCommandSet.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs @@ -268,6 +343,12 @@ Microsoft\Data\SqlClient\SqlConnectionString.cs + + Microsoft\Data\SqlClient\SqlConnectionStringBuilder.cs + + + Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs + Microsoft\Data\SqlClient\SqlCredential.cs @@ -280,21 +361,42 @@ Microsoft\Data\SqlClient\SqlDependencyListener.cs + + Microsoft\Data\SqlClient\SqlDependencyUtils.cs + + + Microsoft\Data\SqlClient\SqlDependencyUtils.AppDomain.cs + + + Microsoft\Data\SqlClient\SqlEnclaveSession.cs + Microsoft\Data\SqlClient\SqlEnums.cs + + Microsoft\Data\SqlClient\SqlEnvChange.cs + + + Microsoft\Data\SqlClient\SqlError.cs + Microsoft\Data\SqlClient\SqlErrorCollection.cs Microsoft\Data\SqlClient\SqlException.cs + + Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs + Microsoft\Data\SqlClient\SqlInfoMessageEvent.cs Microsoft\Data\SqlClient\SqlInfoMessageEventHandler.cs + + Microsoft\Data\SqlClient\SqlInternalTransaction.cs + Microsoft\Data\SqlClient\SqlMetadataFactory.cs @@ -310,12 +412,15 @@ Microsoft\Data\SqlClient\SqlNotificationType.cs - - Microsoft\Data\SqlClient\SqlParameterCollection.cs - Microsoft\Data\SqlClient\SqlObjectPool.cs + + Microsoft\Data\SqlClient\SqlParameter.cs + + + Microsoft\Data\SqlClient\SqlParameterCollection.cs + Microsoft\Data\SqlClient\SqlQueryMetadataCache.cs @@ -336,95 +441,48 @@ Microsoft\Data\SqlClient\SqlSecurityUtility.cs - - - Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs - - - Microsoft\Data\SQLTypes\SQLResource.cs - - - Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs - - Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs - - - Microsoft\Data\OperationAbortedException.cs + + Microsoft\Data\SqlClient\SqlSequentialStream.cs - - Microsoft\Data\DataException.cs + + Microsoft\Data\SqlClient\Server\SqlSer.cs - - Microsoft\Data\SqlClient\Server\InvalidUdtException.cs + + Microsoft\Data\SqlClient\SqlStatistics.cs - - Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs + + Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs - - Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs + + Microsoft\Data\SqlClient\SqlUdtInfo.cs - - Microsoft\Data\SqlClient\SignatureVerificationCache.cs + + Microsoft\Data\SqlClient\SqlUtil.cs - - Microsoft\Data\SqlClient\TdsValueSetter.cs + + Microsoft\Data\SqlClient\TdsEnums.cs Microsoft\Data\SqlClient\TdsParameterSetter.cs + + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs + Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs - - Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs - - - Microsoft\Data\SqlClient\Server\SmiTypedGetterSetter.cs - - - Microsoft\Data\SqlClient\Server\SmiEventSink.cs - - - Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalBaseEnumerator.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogic.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBase.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBaseProvider.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicProvider.cs - - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryFactory.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalEnumerators.cs - - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + Microsoft\Data\SqlClient\TdsValueSetter.cs - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + Microsoft\Data\SQLTypes\SQLResource.cs - - Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs - - - - Microsoft\Data\SqlClient\SqlUtil.cs + + Microsoft\Data\SqlClient\SqlStream.cs Resources\ResCategoryAttribute.cs @@ -435,11 +493,8 @@ Resources\StringsHelper.cs - - Microsoft\Data\SqlClient\SqlConnectionStringBuilder.cs - - - Microsoft\Data\Common\AdapterUtil.cs + + Common\System\Diagnostics\CodeAnalysis.cs @@ -454,37 +509,32 @@ + + - - - - Microsoft\Data\SqlClient\AlwaysEncryptedAttestationException.cs - Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs - + + Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs + + + Microsoft\Data\SqlClient\EnclaveDelegate.Crypto.cs + Microsoft\Data\SqlClient\EnclaveProviderBase.cs Microsoft\Data\SqlClient\EnclaveSessionCache.cs - Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.Crypto.cs - - Microsoft\Data\SqlClient\EnclaveDelegate.Crypto.cs - - - Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs - Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs @@ -494,21 +544,25 @@ Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs + + + + Microsoft\Data\SqlClient\SqlDependencyUtils.AssemblyLoadContext.cs + + + - + + - - - - @@ -524,116 +578,94 @@ Strings.Designer.cs System - - - - - - Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs + + Common\CoreLib\System\Threading\Tasks\TaskToApm.cs + + + Common\Microsoft\Data\ProviderBase\DbConnectionClosed.cs Common\Microsoft\Data\ProviderBase\DbConnectionFactory.cs - - Common\Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs + + Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs Common\Microsoft\Data\ProviderBase\DbReferenceCollection.cs - - Common\Microsoft\Data\ProviderBase\DbConnectionClosed.cs - + + + - - Microsoft\Data\SqlClient\EnclaveDelegate.cs - - - Microsoft\Data\SqlClient\EnclavePackage.cs - - - Microsoft\Data\SqlClient\SqlSequentialStream.cs - - - + + - + + + + + + + + + + + + + + + + + + + + - - Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs - - - + - - - Microsoft\Data\SqlClient\SqlEnclaveSession.cs - - - - - - Microsoft\Data\SqlClient\SqlStatistics.cs - - - - Microsoft\Data\SqlClient\SqlUdtInfo.cs - - - - - - Common\CoreLib\System\Threading\Tasks\TaskToApm.cs - - - - - - - - - - - - - - - - - - - - - + + + Microsoft\Data\Common\AdapterUtil.Windows.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs + + + Microsoft\Data\Common\AdapterUtil.Unix.cs + @@ -662,14 +694,14 @@ Common\Interop\Windows\Interop.UNICODE_STRING.cs - - Common\Interop\Windows\Kernel32\Interop.IoControlCodeAccess.cs - Common\Interop\Windows\Kernel32\Interop.CTL_CODE.cs - Common\Interop\Windows\kernel32\Interop.DeviceIoControl.cs + Common\Interop\Windows\Kernel32\Interop.DeviceIoControl.cs + + + Common\Interop\Windows\Kernel32\Interop.IoControlCodeAccess.cs Common\Interop\Windows\Kernel32\Interop.IoControlTransferType.cs @@ -684,219 +716,220 @@ Common\Interop\Windows\NtDll\Interop.NtCreateFile.cs - Common\Interop\Windows\Interop.RtlNtStatusToDosError.cs + Common\Interop\Windows\NtDll\Interop.RtlNtStatusToDosError.cs - + - - - - + + + + - + + Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs + - + - + + - - Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs - - + + Common\CoreLib\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs + Common\Interop\Windows\kernel32\Interop.FreeLibrary.cs Common\Interop\Windows\kernel32\Interop.GetProcAddress.cs - - Common\CoreLib\Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs - + + + Common\CoreLib\Interop\Windows\Kernel32\Interop.CloseHandle.cs + + + Common\Interop\Windows\Crypt32\Interop.certificates.cs + + + Common\Interop\Windows\Crypt32\Interop.certificates_types.cs + + + Common\Interop\Windows\Interop.Libraries.cs + Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs - - Common\System\Net\Security\NegotiateStreamPal.Windows.cs + + Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs - - Common\Interop\Windows\sspicliSafeDeleteContext.cs + + Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs - - Common\Interop\Windows\sspicli\SecuritySafeHandles.cs + + Common\Interop\Windows\sspicli\GlobalSSPI.cs Common\Interop\Windows\sspicli\Interop.SSPI.cs - - Common\System\Net\Security\SecurityContextTokenHandle.cs - - - Common\Interop\Windows\Interop.Libraries.cs + + Common\Interop\Windows\sspicli\NegotiationInfoClass.cs - - Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs + + Common\Interop\Windows\sspicli\SafeDeleteContext.cs Common\Interop\Windows\sspicli\SecPkgContext_Bindings.cs - - Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs - - - Common\System\Net\Security\NetEventSource.Security.cs + + Common\Interop\Windows\sspicli\SecPkgContext_NegotiationInfoW.cs - - Common\CoreLib\Interop\Windows\Kernel32\Interop.CloseHandle.cs + + Common\Interop\Windows\sspicli\SecPkgContext_Sizes.cs - - Common\Interop\Windows\sspicli\GlobalSSPI.cs + + Common\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs - - Common\Interop\Windows\sspicli\SSPIInterface.cs + + Common\Interop\Windows\sspicli\SecurityPackageInfo.cs Common\Interop\Windows\sspicli\SecurityPackageInfoClass.cs - - Common\Interop\Windows\sspicli\SecurityPackageInfo.cs + + Common\Interop\Windows\sspicli\SecuritySafeHandles.cs Common\Interop\Windows\sspicli\SSPIAuthType.cs + + Common\Interop\Windows\sspicli\SSPIInterface.cs + Common\Interop\Windows\sspicli\SSPISecureChannelType.cs Common\Interop\Windows\sspicli\SSPIWrapper.cs - - Common\System\Net\Security\NetEventSource.Security.Windows.cs - - - Common\Interop\Windows\sspicli\SecPkgContext_Sizes.cs - - - Common\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs + + Common\System\Collections\Generic\BidirectionalDictionary.cs - - Common\Interop\Windows\sspicli\SecPkgContext_NegotiationInfoW.cs + + Common\System\Net\ContextFlagsAdapterPal.Windows.cs - - Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs + + Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs - - Common\Interop\Windows\sspicli\NegotiationInfoClass.cs + + Common\System\Net\Security\NegotiateStreamPal.Windows.cs - - Common\Interop\Windows\Crypt32\Interop.certificates.cs + + Common\System\Net\Security\NetEventSource.Security.cs - - Common\Interop\Windows\Crypt32\Interop.certificates_types.cs + + Common\System\Net\Security\NetEventSource.Security.Windows.cs - - Common\System\Net\ContextFlagsAdapterPal.Windows.cs + + Common\System\Net\Security\SecurityContextTokenHandle.cs Common\System\Net\SecurityStatusAdapterPal.Windows.cs - - Common\System\Collections\Generic\BidirectionalDictionary.cs - Common\System\Net\ContextFlagsPal.cs - - Common\System\Net\SecurityStatusPal.cs - - - Common\System\Net\Security\SecurityBufferType.cs - - - Common\System\Net\Security\SecurityBuffer.cs + + Common\System\Net\DebugCriticalHandleMinusOneIsInvalid.cs Common\System\Net\DebugSafeHandle.cs - - Common\System\Net\DebugCriticalHandleMinusOneIsInvalid.cs - - - Common\System\Net\Logging\NetEventSource.Common.cs + + Common\System\Net\InternalException.cs Common\System\Net\Logging\DebugThreadTracking.cs - - Common\System\Net\InternalException.cs + + Common\System\Net\Logging\NetEventSource.Common.cs Common\System\Net\NegotiationInfoClass.cs + + Common\System\Net\Security\SecurityBuffer.cs + + + Common\System\Net\Security\SecurityBufferType.cs + + + Common\System\Net\SecurityStatusPal.cs + - - - - Common\System\Net\Security\NegotiateStreamPal.Unix.cs - - - Common\System\Net\Security\Unix\SafeDeleteContext.cs + + Common\Interop\Unix\Interop.Libraries.cs - - Common\System\Net\Security\Unix\SafeFreeCredentials.cs + + Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs - - Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs + + Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs - - Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs + + Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs - - Common\Interop\Unix\Interop.Libraries.cs + + Common\System\Net\ContextFlagsAdapterPal.Unix.cs - - Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs + + Common\System\Net\Security\NegotiateStreamPal.Unix.cs + + + Common\System\Net\Security\Unix\SafeDeleteContext.cs Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs - - Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs + + Common\System\Net\Security\Unix\SafeFreeCredentials.cs - - Common\System\Net\ContextFlagsAdapterPal.Unix.cs + + Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs - - + + + - + + + @@ -918,6 +951,9 @@ + + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs index 11c153e5b3..c85d042b2a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs @@ -11,14 +11,6 @@ namespace Microsoft.Data.ProviderBase { sealed internal partial class DbConnectionPool { - partial void CheckPoolBlockingPeriod(Exception e) - { - if (!IsBlockingPeriodEnabled()) - { - throw e; - } - } - private bool IsBlockingPeriodEnabled() { var poolGroupConnectionOptions = _connectionPoolGroup.ConnectionOptions as SqlConnectionString; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index c6f5a39693..7858adc93c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -776,7 +776,12 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio throw; } - CheckPoolBlockingPeriod(e); +#if NETCOREAPP + if (!IsBlockingPeriodEnabled()) + { + throw; + } +#endif // Close associated Parser if connection already established. if (newObj?.IsConnectionAlive() == true) @@ -824,9 +829,6 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio return newObj; } - //This method is implemented in DbConnectionPool.NetCoreApp - partial void CheckPoolBlockingPeriod(Exception e); - private void DeactivateObject(DbConnectionInternal obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Deactivating.", ObjectID, obj.ObjectID); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs new file mode 100644 index 0000000000..6ee3fe3329 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Unix.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System.Data; +using System.Data.Common; +using Microsoft.Data.SqlClient.Server; + +namespace Microsoft.Data.Sql +{ + /// + public sealed partial class SqlDataSourceEnumerator : DbDataSourceEnumerator + { + private partial DataTable GetDataSourcesInternal() => SqlDataSourceEnumeratorManagedHelper.GetDataSources(); + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs index 6ec654e3bf..1b4679bfe4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.Windows.cs @@ -22,14 +22,13 @@ private static IntPtr UserInstanceDLLHandle if (s_userInstanceDLLHandle == IntPtr.Zero) { SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle); - if(s_userInstanceDLLHandle != IntPtr.Zero) + if (s_userInstanceDLLHandle != IntPtr.Zero) { SqlClientEventSource.Log.TryTraceEvent("LocalDBAPI.UserInstanceDLLHandle | LocalDB - handle obtained"); } else { - SNINativeMethodWrapper.SNI_Error sniError; - SNINativeMethodWrapper.SNIGetLastError(out sniError); + SNINativeMethodWrapper.SNIGetLastError(out SNINativeMethodWrapper.SNI_Error sniError); throw CreateLocalDBException(errorMessage: StringsHelper.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs index 4f3fd13dce..ba2371c232 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/LocalDBAPI.cs @@ -10,25 +10,37 @@ namespace Microsoft.Data { internal static partial class LocalDBAPI { - private const string const_localDbPrefix = @"(localdb)\"; + private const string LocalDbPrefix = @"(localdb)\"; + private const string LocalDbPrefix_NP = @"np:\\.\pipe\LOCALDB#"; [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, uint dwFlags, uint dwLanguageId, StringBuilder buffer, ref uint buflen); // check if name is in format (localdb)\ and return instance name if it is + // localDB can also have a format of np:\\.\pipe\LOCALDB#\tsql\query internal static string GetLocalDbInstanceNameFromServerName(string serverName) { - if (serverName == null) - return null; - serverName = serverName.TrimStart(); // it can start with spaces if specified in quotes - if (!serverName.StartsWith(const_localDbPrefix, StringComparison.OrdinalIgnoreCase)) - return null; - string instanceName = serverName.Substring(const_localDbPrefix.Length).Trim(); - if (instanceName.Length == 0) - return null; - else - return instanceName; + if (serverName is not null) + { + // it can start with spaces if specified in quotes + // Memory allocation is reduced by using ReadOnlySpan + ReadOnlySpan input = serverName.AsSpan().Trim(); + if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + input = input.Slice(LocalDbPrefix.Length); + if (!input.IsEmpty) + { + return input.ToString(); + } + } + else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return input.ToString(); + } + + } + return null; } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs index b57dc4f5f3..815f4e13d7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -108,6 +109,7 @@ internal class SNICommon internal const int ConnTimeoutError = 11; internal const int ConnNotUsableError = 19; internal const int InvalidConnStringError = 25; + internal const int ErrorLocatingServerInstance = 26; internal const int HandshakeFailureError = 31; internal const int InternalExceptionError = 35; internal const int ConnOpenFailedError = 40; @@ -193,6 +195,15 @@ internal static bool ValidateSslServerCertificate(string targetServerName, X509C } } + internal static IPAddress[] GetDnsIpAddresses(string serverName) + { + using (TrySNIEventScope.Create(nameof(GetDnsIpAddresses))) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNICommon), EventType.INFO, "Getting DNS host entries for serverName {0}.", args0: serverName); + return Dns.GetHostAddresses(serverName); + } + } + /// /// Sets last error encountered for SNI /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs index dbee403f41..7613817a23 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIHandle.cs @@ -3,7 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Net.Security; using System.Security.Authentication; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Data.SqlClient.SNI { @@ -13,15 +18,39 @@ namespace Microsoft.Data.SqlClient.SNI internal abstract class SNIHandle { /// - /// Exclude TLS 1.3 (not fully supported). + /// Exclude TLS 1.3 in TLS-over-TDS modes (TDS 7.4 and below) /// - protected readonly SslProtocols SupportedProtocols = LocalAppContextSwitches.UseSystemDefaultSecureProtocols ? SslProtocols.None : SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls + protected static readonly SslProtocols s_supportedProtocols = LocalAppContextSwitches.UseSystemDefaultSecureProtocols ? SslProtocols.None : SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls //protected readonly SslProtocols SupportedProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls #pragma warning disable CS0618 // Type or member is obsolete | SslProtocols.Ssl2 | SslProtocols.Ssl3 #pragma warning restore CS0618 // Type or member is obsolete ; +#if !NETSTANDARD2_0 + protected static readonly List s_tdsProtocols = new List(1) { new(TdsEnums.TDS8_Protocol) }; + + protected static async Task AuthenticateAsClientAsync(SslStream sslStream, string serverNameIndication, X509CertificateCollection certificate, CancellationToken token) + { + SslClientAuthenticationOptions sslClientOptions = new() + { + TargetHost = serverNameIndication, + ApplicationProtocols = s_tdsProtocols, + ClientCertificates = certificate + }; + await sslStream.AuthenticateAsClientAsync(sslClientOptions, token); + } +#endif + + protected static void AuthenticateAsClient(SslStream sslStream, string serverNameIndication, X509CertificateCollection certificate) + { +#if !NETSTANDARD2_0 + AuthenticateAsClientAsync(sslStream, serverNameIndication, certificate, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); +#else + throw new NotSupportedException(Strings.SQL_TDS8_NotSupported_Netstandard2_0); +#endif + } + /// /// Dispose class /// @@ -51,9 +80,8 @@ internal abstract class SNIHandle /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public abstract uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null); + public abstract uint SendAsync(SNIPacket packet); /// /// Receive a packet synchronously diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs index 395cfed4be..90bf83dacc 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsConnection.cs @@ -112,16 +112,15 @@ public uint Send(SNIPacket packet) /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public uint SendAsync(SNIPacket packet, SNIAsyncCallback callback) + public uint SendAsync(SNIPacket packet) { long scopeID = SqlClientEventSource.Log.TrySNIScopeEnterEvent(s_className); try { lock (this) { - return _lowerHandle.SendAsync(packet, callback); + return _lowerHandle.SendAsync(packet); } } finally @@ -192,7 +191,7 @@ public void HandleReceiveError(SNIPacket packet) Debug.Assert(Monitor.IsEntered(this), "HandleReceiveError was called without being locked."); foreach (SNIMarsHandle handle in _sessions.Values) { - if (packet.HasCompletionCallback) + if (packet.HasAsyncIOCompletionCallback) { handle.HandleReceiveError(packet); #if DEBUG @@ -215,7 +214,7 @@ public void HandleReceiveError(SNIPacket packet) /// SNI error code public void HandleSendComplete(SNIPacket packet, uint sniErrorCode) { - packet.InvokeCompletionCallback(sniErrorCode); + packet.InvokeAsyncIOCompletionCallback(sniErrorCode); } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs index 62eff5accc..8246ce3d6f 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsHandle.cs @@ -19,7 +19,7 @@ internal sealed class SNIMarsHandle : SNIHandle private readonly SNIMarsConnection _connection; private readonly uint _status = TdsEnums.SNI_UNINITIALIZED; private readonly Queue _receivedPacketQueue = new Queue(); - private readonly Queue _sendPacketQueue = new Queue(); + private readonly Queue _sendPacketQueue = new Queue(); private readonly object _callbackObject; private readonly Guid _connectionId; private readonly ushort _sessionId; @@ -191,9 +191,8 @@ public override uint Send(SNIPacket packet) /// Send packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) + private uint InternalSendAsync(SNIPacket packet) { Debug.Assert(packet.ReservedHeaderSize == SNISMUXHeader.HEADER_LENGTH, "mars handle attempting to send muxed packet without smux reservation in InternalSendAsync"); using (TrySNIEventScope.Create("SNIMarsHandle.InternalSendAsync | SNI | INFO | SCOPE | Entering Scope {0}")) @@ -207,9 +206,9 @@ private uint InternalSendAsync(SNIPacket packet, SNIAsyncCallback callback) } SNIPacket muxedPacket = SetPacketSMUXHeader(packet); - muxedPacket.SetCompletionCallback(callback ?? HandleSendComplete); + muxedPacket.SetAsyncIOCompletionCallback(_handleSendCompleteCallback); SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIMarsHandle), EventType.INFO, "MARS Session Id {0}, _sequenceNumber {1}, _sendHighwater {2}, Sending packet", args0: ConnectionId, args1: _sequenceNumber, args2: _sendHighwater); - return _connection.SendAsync(muxedPacket, callback); + return _connection.SendAsync(muxedPacket); } } } @@ -222,7 +221,7 @@ private uint SendPendingPackets() { using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { - SNIMarsQueuedPacket packet = null; + SNIPacket packet = null; while (true) { @@ -233,7 +232,7 @@ private uint SendPendingPackets() if (_sendPacketQueue.Count != 0) { packet = _sendPacketQueue.Peek(); - uint result = InternalSendAsync(packet.Packet, packet.Callback); + uint result = InternalSendAsync(packet); if (result != TdsEnums.SNI_SUCCESS && result != TdsEnums.SNI_SUCCESS_IO_PENDING) { @@ -264,15 +263,15 @@ private uint SendPendingPackets() /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { using (TrySNIEventScope.Create(nameof(SNIMarsHandle))) { + packet.SetAsyncIOCompletionCallback(_handleSendCompleteCallback); lock (this) { - _sendPacketQueue.Enqueue(new SNIMarsQueuedPacket(packet, callback ?? _handleSendCompleteCallback)); + _sendPacketQueue.Enqueue(packet); } SendPendingPackets(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs deleted file mode 100644 index 0f97eb4978..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIMarsQueuedPacket.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlClient.SNI -{ - /// - /// Mars queued packet - /// - internal sealed class SNIMarsQueuedPacket - { - private readonly SNIPacket _packet; - private readonly SNIAsyncCallback _callback; - - /// - /// Constructor - /// - /// SNI packet - /// Completion callback - public SNIMarsQueuedPacket(SNIPacket packet, SNIAsyncCallback callback) - { - _packet = packet; - _callback = callback; - } - - public SNIPacket Packet => _packet; - - public SNIAsyncCallback Callback => _callback; - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs index 2b93f4e752..b48ea36958 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNINpHandle.cs @@ -23,7 +23,7 @@ internal sealed class SNINpHandle : SNIPhysicalHandle private readonly string _targetServer; private readonly object _sendSync; - + private readonly bool _tlsFirst; private Stream _stream; private NamedPipeClientStream _pipeStream; private SslOverTdsStream _sslOverTdsStream; @@ -37,7 +37,7 @@ internal sealed class SNINpHandle : SNIPhysicalHandle private int _bufferSize = TdsEnums.DEFAULT_LOGIN_PACKET_SIZE; private readonly Guid _connectionId = Guid.NewGuid(); - public SNINpHandle(string serverName, string pipeName, long timerExpire) + public SNINpHandle(string serverName, string pipeName, long timerExpire, bool tlsFirst) { using (TrySNIEventScope.Create(nameof(SNINpHandle))) { @@ -45,7 +45,7 @@ public SNINpHandle(string serverName, string pipeName, long timerExpire) _sendSync = new object(); _targetServer = serverName; - + _tlsFirst = tlsFirst; try { _pipeStream = new NamedPipeClientStream( @@ -90,8 +90,14 @@ public SNINpHandle(string serverName, string pipeName, long timerExpire) return; } - _sslOverTdsStream = new SslOverTdsStream(_pipeStream, _connectionId); - _sslStream = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + Stream stream = _pipeStream; + + if (!_tlsFirst) + { + _sslOverTdsStream = new SslOverTdsStream(_pipeStream, _connectionId); + stream = _sslOverTdsStream; + } + _sslStream = new SNISslStream(stream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); _stream = _pipeStream; _status = TdsEnums.SNI_SUCCESS; @@ -210,10 +216,10 @@ public override uint ReceiveAsync(ref SNIPacket packet) { SNIPacket errorPacket; packet = RentPacket(headerSize: 0, dataSize: _bufferSize); - + packet.SetAsyncIOCompletionCallback(_receiveCallback); try { - packet.ReadFromStreamAsync(_stream, _receiveCallback); + packet.ReadFromStreamAsync(_stream); SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Rented and read packet asynchronously, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); return TdsEnums.SNI_SUCCESS_IO_PENDING; } @@ -288,13 +294,12 @@ public override uint Send(SNIPacket packet) } } - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { using (TrySNIEventScope.Create(nameof(SNINpHandle))) { - SNIAsyncCallback cb = callback ?? _sendCallback; SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNINpHandle), EventType.INFO, "Connection Id {0}, Packet writing to stream, dataLeft {1}", args0: _connectionId, args1: packet?.DataLeft); - packet.WriteToStreamAsync(_stream, cb, SNIProviders.NP_PROV); + packet.WriteToStreamAsync(_stream, _sendCallback, SNIProviders.NP_PROV); return TdsEnums.SNI_SUCCESS_IO_PENDING; } } @@ -312,8 +317,19 @@ public override uint EnableSsl(uint options) _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; try { - _sslStream.AuthenticateAsClient(_targetServer, null, SupportedProtocols, true); - _sslOverTdsStream.FinishHandshake(); + if (_tlsFirst) + { + AuthenticateAsClient(_sslStream, _targetServer, null); + } + else + { + // TODO: Resolve whether to send _serverNameIndication or _targetServer. _serverNameIndication currently results in error. Why? + _sslStream.AuthenticateAsClient(_targetServer, null, s_supportedProtocols, false); + } + if (_sslOverTdsStream is not null) + { + _sslOverTdsStream.FinishHandshake(); + } } catch (AuthenticationException aue) { @@ -334,7 +350,7 @@ public override void DisableSsl() { _sslStream.Dispose(); _sslStream = null; - _sslOverTdsStream.Dispose(); + _sslOverTdsStream?.Dispose(); _sslOverTdsStream = null; _stream = _pipeStream; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs index 58ac68c7c4..d83682021b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPacket.cs @@ -6,7 +6,6 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; @@ -19,14 +18,14 @@ namespace Microsoft.Data.SqlClient.SNI /// internal sealed class SNIPacket { + private static readonly Action, object> s_readCallback = ReadFromStreamAsyncContinuation; private int _dataLength; // the length of the data in the data segment, advanced by Append-ing data, does not include smux header length private int _dataCapacity; // the total capacity requested, if the array is rented this may be less than the _data.Length, does not include smux header length private int _dataOffset; // the start point of the data in the data segment, advanced by Take-ing data private int _headerLength; // the amount of space at the start of the array reserved for the smux header, this is zeroed in SetHeader // _headerOffset is not needed because it is always 0 private byte[] _data; - private SNIAsyncCallback _completionCallback; - private readonly Action, object> _readCallback; + private SNIAsyncCallback _asyncIOCompletionCallback; #if DEBUG internal readonly int _id; // in debug mode every packet is assigned a unique id so that the entire lifetime can be tracked when debugging /// refcount = 0 means that a packet should only exist in the pool @@ -85,7 +84,6 @@ public SNIPacket(SNIHandle owner, int id) #endif public SNIPacket() { - _readCallback = ReadFromStreamAsyncContinuation; } /// @@ -110,25 +108,19 @@ public SNIPacket() public int ReservedHeaderSize => _headerLength; - public bool HasCompletionCallback => !(_completionCallback is null); + public bool HasAsyncIOCompletionCallback => _asyncIOCompletionCallback is not null; /// - /// Set async completion callback + /// Set async receive callback /// - /// Completion callback - public void SetCompletionCallback(SNIAsyncCallback completionCallback) - { - _completionCallback = completionCallback; - } + /// Completion callback + public void SetAsyncIOCompletionCallback(SNIAsyncCallback asyncIOCompletionCallback) => _asyncIOCompletionCallback = asyncIOCompletionCallback; /// - /// Invoke the completion callback + /// Invoke the receive callback /// /// SNI error - public void InvokeCompletionCallback(uint sniErrorCode) - { - _completionCallback(this, sniErrorCode); - } + public void InvokeAsyncIOCompletionCallback(uint sniErrorCode) => _asyncIOCompletionCallback(this, sniErrorCode); /// /// Allocate space for data @@ -253,7 +245,7 @@ public void Release() _dataLength = 0; _dataOffset = 0; _headerLength = 0; - _completionCallback = null; + _asyncIOCompletionCallback = null; IsOutOfBand = false; } @@ -273,49 +265,48 @@ public void ReadFromStream(Stream stream) /// Read data from a stream asynchronously /// /// Stream to read from - /// Completion callback - public void ReadFromStreamAsync(Stream stream, SNIAsyncCallback callback) + public void ReadFromStreamAsync(Stream stream) { stream.ReadAsync(_data, 0, _dataCapacity, CancellationToken.None) .ContinueWith( - continuationAction: _readCallback, - state: callback, + continuationAction: s_readCallback, + state: this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default ); } - private void ReadFromStreamAsyncContinuation(Task t, object state) + private static void ReadFromStreamAsyncContinuation(Task task, object state) { - SNIAsyncCallback callback = (SNIAsyncCallback)state; + SNIPacket packet = (SNIPacket)state; bool error = false; - Exception e = t.Exception?.InnerException; + Exception e = task.Exception?.InnerException; if (e != null) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.InternalExceptionError, e); #if DEBUG - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, Internal Exception occurred while reading data: {1}", args0: _owner?.ConnectionId, args1: e?.Message); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, Internal Exception occurred while reading data: {1}", args0: packet._owner?.ConnectionId, args1: e?.Message); #endif error = true; } else { - _dataLength = t.Result; + packet._dataLength = task.Result; #if DEBUG - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} read from stream.", args0: _owner?.ConnectionId, args1: _id, args2: _dataLength); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.INFO, "Connection Id {0}, Packet Id {1} _dataLength {2} read from stream.", args0: packet._owner?.ConnectionId, args1: packet._id, args2: packet._dataLength); #endif - if (_dataLength == 0) + if (packet._dataLength == 0) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, 0, SNICommon.ConnTerminatedError, Strings.SNI_ERROR_2); #if DEBUG - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, No data read from stream, connection was terminated.", args0: _owner?.ConnectionId); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIPacket), EventType.ERR, "Connection Id {0}, No data read from stream, connection was terminated.", args0: packet._owner?.ConnectionId); #endif error = true; } } - callback(this, error ? TdsEnums.SNI_ERROR : TdsEnums.SNI_SUCCESS); + packet.InvokeAsyncIOCompletionCallback(error ? TdsEnums.SNI_ERROR : TdsEnums.SNI_SUCCESS); } /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs index ba08f99bea..94f37d0c6a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIPhysicalHandle.cs @@ -12,6 +12,7 @@ namespace Microsoft.Data.SqlClient.SNI internal abstract class SNIPhysicalHandle : SNIHandle { protected const int DefaultPoolSize = 4; + #if DEBUG private static int s_packetId; #endif @@ -84,7 +85,7 @@ private string GetStackParts() { return string.Join(Environment.NewLine, Environment.StackTrace - .Split(new string[] { Environment.NewLine },StringSplitOptions.None) + .Split(new string[] { Environment.NewLine }, StringSplitOptions.None) .Skip(3) // trims off the common parts at the top of the stack so you can see what the actual caller was .Take(7) // trims off most of the bottom of the stack because when running under xunit there's a lot of spam ); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs index 0af8441333..d94874f908 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs @@ -9,7 +9,6 @@ using System.Net.Security; using System.Net.Sockets; using System.Text; -using System.Text.RegularExpressions; namespace Microsoft.Data.SqlClient.SNI { @@ -69,7 +68,7 @@ internal static void GenSspiClientContext(SspiClientContextStatus sspiClientCont string[] serverSPNs = new string[serverName.Length]; for (int i = 0; i < serverName.Length; i++) { - serverSPNs[i] = Encoding.UTF8.GetString(serverName[i]); + serverSPNs[i] = Encoding.Unicode.GetString(serverName[i]); } SecurityStatusPal statusCode = NegotiateStreamPal.InitializeSecurityContext( credentialsHandle, @@ -135,16 +134,33 @@ private static bool IsErrorStatus(SecurityStatusPalErrorCode errorCode) /// Timer expiration /// Instance name /// SPN + /// pre-defined SPN /// Flush packet cache /// Asynchronous connection /// Attempt parallel connects /// /// IP address preference /// Used for DNS Cache - /// Used for DNS Cache + /// Used for DNS Cache + /// + /// /// SNI handle - internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, - bool flushCache, bool async, bool parallel, bool isIntegratedSecurity, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + internal static SNIHandle CreateConnectionHandle( + string fullServerName, + bool ignoreSniOpenTimeout, + long timerExpire, + out byte[] instanceName, + ref byte[][] spnBuffer, + string serverSPN, + bool flushCache, + bool async, + bool parallel, + bool isIntegratedSecurity, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate) { instanceName = new byte[1]; @@ -155,7 +171,6 @@ internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ign { return null; } - // If a localDB Data source is available, we need to use it. fullServerName = localDBDataSource ?? fullServerName; @@ -171,10 +186,11 @@ internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ign case DataSource.Protocol.Admin: case DataSource.Protocol.None: // default to using tcp if no protocol is provided case DataSource.Protocol.TCP: - sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo); + sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, + tlsFirst, hostNameInCertificate); break; case DataSource.Protocol.NP: - sniHandle = CreateNpHandle(details, timerExpire, parallel); + sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst); break; default: Debug.Fail($"Unexpected connection protocol: {details._connectionProtocol}"); @@ -185,7 +201,7 @@ internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ign { try { - spnBuffer = GetSqlServerSPNs(details); + spnBuffer = GetSqlServerSPNs(details, serverSPN); } catch (Exception e) { @@ -197,9 +213,13 @@ internal static SNIHandle CreateConnectionHandle(string fullServerName, bool ign return sniHandle; } - private static byte[][] GetSqlServerSPNs(DataSource dataSource) + private static byte[][] GetSqlServerSPNs(DataSource dataSource, string serverSPN) { Debug.Assert(!string.IsNullOrWhiteSpace(dataSource.ServerName)); + if (!string.IsNullOrWhiteSpace(serverSPN)) + { + return new byte[1][] { Encoding.Unicode.GetBytes(serverSPN) }; + } string hostName = dataSource.ServerName; string postfix = null; @@ -247,12 +267,12 @@ private static byte[][] GetSqlServerSPNs(string hostNameOrAddress, string portOr string serverSpnWithDefaultPort = serverSpn + $":{DefaultSqlServerPort}"; // Set both SPNs with and without Port as Port is optional for default instance SqlClientEventSource.Log.TryAdvancedTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerSPNs {0} and {1}", serverSpn, serverSpnWithDefaultPort); - return new byte[][] { Encoding.UTF8.GetBytes(serverSpn), Encoding.UTF8.GetBytes(serverSpnWithDefaultPort) }; + return new byte[][] { Encoding.Unicode.GetBytes(serverSpn), Encoding.Unicode.GetBytes(serverSpnWithDefaultPort) }; } // else Named Pipes do not need to valid port SqlClientEventSource.Log.TryAdvancedTraceEvent("SNIProxy.GetSqlServerSPN | Info | ServerSPN {0}", serverSpn); - return new byte[][] { Encoding.UTF8.GetBytes(serverSpn) }; + return new byte[][] { Encoding.Unicode.GetBytes(serverSpn) }; } /// @@ -263,9 +283,19 @@ private static byte[][] GetSqlServerSPNs(string hostNameOrAddress, string portOr /// Should MultiSubnetFailover be used /// IP address preference /// Key for DNS Cache - /// Used for DNS Cache + /// Used for DNS Cache + /// + /// /// SNITCPHandle - private static SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + private static SNITCPHandle CreateTcpHandle( + DataSource details, + long timerExpire, + bool parallel, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate) { // TCP Format: // tcp:\ @@ -285,12 +315,12 @@ private static SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire try { port = isAdminConnection ? - SSRP.GetDacPortByInstanceName(hostName, details.InstanceName) : - SSRP.GetPortByInstanceName(hostName, details.InstanceName); + SSRP.GetDacPortByInstanceName(hostName, details.InstanceName, timerExpire, parallel, ipPreference) : + SSRP.GetPortByInstanceName(hostName, details.InstanceName, timerExpire, parallel, ipPreference); } catch (SocketException se) { - SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.InvalidConnStringError, se); + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.TCP_PROV, SNICommon.ErrorLocatingServerInstance, se); return null; } } @@ -303,7 +333,8 @@ private static SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire port = isAdminConnection ? DefaultSqlServerDacPort : DefaultSqlServerPort; } - return new SNITCPHandle(hostName, port, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo); + return new SNITCPHandle(hostName, port, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, + tlsFirst, hostNameInCertificate); } /// @@ -312,8 +343,9 @@ private static SNITCPHandle CreateTcpHandle(DataSource details, long timerExpire /// Data source /// Timer expiration /// Should MultiSubnetFailover be used. Only returns an error for named pipes. + /// /// SNINpHandle - private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel) + private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, bool parallel, bool tlsFirst) { if (parallel) { @@ -321,7 +353,7 @@ private static SNINpHandle CreateNpHandle(DataSource details, long timerExpire, SNICommon.ReportSNIError(SNIProviders.NP_PROV, 0, SNICommon.MultiSubnetFailoverWithNonTcpProtocol, Strings.SNI_ERROR_49); return null; } - return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire); + return new SNINpHandle(details.PipeHostName, details.PipeName, timerExpire, tlsFirst); } /// @@ -342,8 +374,7 @@ internal SNIError GetLastError() private static string GetLocalDBDataSource(string fullServerName, out bool error) { string localDBConnectionString = null; - bool isBadLocalDBDataSource; - string localDBInstance = DataSource.GetLocalDBInstance(fullServerName, out isBadLocalDBDataSource); + string localDBInstance = DataSource.GetLocalDBInstance(fullServerName, out bool isBadLocalDBDataSource); if (isBadLocalDBDataSource) { @@ -381,6 +412,7 @@ internal class DataSource private const string Slash = @"/"; private const string PipeToken = "pipe"; private const string LocalDbHost = "(localdb)"; + private const string LocalDbHost_NP = @"np:\\.\pipe\LOCALDB#"; private const string NamedPipeInstanceNameHeader = "mssql$"; private const string DefaultPipeName = "sql\\query"; @@ -482,11 +514,9 @@ private void PopulateProtocol() internal static string GetLocalDBInstance(string dataSource, out bool error) { string instanceName = null; - // ReadOnlySpan is not supported in netstandard 2.0, but installing System.Memory solves the issue ReadOnlySpan input = dataSource.AsSpan().TrimStart(); error = false; - // NetStandard 2.0 does not support passing a string to ReadOnlySpan if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) { @@ -507,6 +537,11 @@ internal static string GetLocalDBInstance(string dataSource, out bool error) error = true; } } + else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + { + instanceName = input.Trim().ToString(); + } + return instanceName; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs index d37ba9d35c..02445d83b1 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNITcpHandle.cs @@ -26,6 +26,8 @@ internal sealed class SNITCPHandle : SNIPhysicalHandle private readonly object _sendSync; private readonly Socket _socket; private NetworkStream _tcpStream; + private readonly string _hostNameInCertificate; + private readonly bool _tlsFirst; private Stream _stream; private SslStream _sslStream; @@ -117,14 +119,27 @@ public override int ProtocolVersion /// Parallel executions /// IP address preference /// Key for DNS Cache - /// Used for DNS Cache - public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo) + /// Used for DNS Cache + /// Support TDS8.0 + /// Host Name in Certoficate + public SNITCPHandle( + string serverName, + int port, + long timerExpire, + bool parallel, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + bool tlsFirst, + string hostNameInCertificate) { using (TrySNIEventScope.Create(nameof(SNITCPHandle))) { SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Setting server name = {1}", args0: _connectionId, args1: serverName); _targetServer = serverName; + _tlsFirst = tlsFirst; + _hostNameInCertificate = hostNameInCertificate; _sendSync = new object(); SQLDNSInfo cachedDNSInfo; @@ -146,9 +161,9 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel bool reportError = true; SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Connecting to serverName {1} and port {2}", args0: _connectionId, args1: serverName, args2: port); - // We will always first try to connect with serverName as before and let the DNS server to resolve the serverName. - // If the DSN resolution fails, we will try with IPs in the DNS cache if existed. We try with cached IPs based on IPAddressPreference. - // The exceptions will be throw to upper level and be handled as before. + // We will always first try to connect with serverName as before and let DNS resolve the serverName. + // If DNS resolution fails, we will try with IPs in the DNS cache if they exist. We try with cached IPs based on IPAddressPreference. + // Exceptions will be thrown to the caller and be handled as before. try { if (parallel) @@ -249,8 +264,13 @@ public SNITCPHandle(string serverName, int port, long timerExpire, bool parallel _socket.NoDelay = true; _tcpStream = new SNINetworkStream(_socket, true); - _sslOverTdsStream = new SslOverTdsStream(_tcpStream, _connectionId); - _sslStream = new SNISslStream(_sslOverTdsStream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); + Stream stream = _tcpStream; + if (!_tlsFirst) + { + _sslOverTdsStream = new SslOverTdsStream(_tcpStream, _connectionId); + stream = _sslOverTdsStream; + } + _sslStream = new SNISslStream(stream, true, new RemoteCertificateValidationCallback(ValidateServerCertificate)); } catch (SocketException se) { @@ -279,9 +299,7 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i Socket availableSocket = null; Task connectTask; - Task serverAddrTask = Dns.GetHostAddressesAsync(hostName); - serverAddrTask.Wait(ts); - IPAddress[] serverAddresses = serverAddrTask.Result; + IPAddress[] serverAddresses = SNICommon.GetDnsIpAddresses(hostName); if (serverAddresses.Length > MaxParallelIpAddresses) { @@ -324,7 +342,6 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i availableSocket = connectTask.Result; return availableSocket; - } // Connect to server with hostName and port. @@ -334,7 +351,7 @@ private static Socket Connect(string serverName, int port, TimeSpan timeout, boo { SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "IP preference : {0}", Enum.GetName(typeof(SqlConnectionIPAddressPreference), ipPreference)); - IPAddress[] ipAddresses = Dns.GetHostAddresses(serverName); + IPAddress[] ipAddresses = SNICommon.GetDnsIpAddresses(serverName); string IPv4String = null; string IPv6String = null; @@ -578,27 +595,41 @@ private static async void ParallelConnectHelper( /// public override uint EnableSsl(uint options) { - _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - - try - { - _sslStream.AuthenticateAsClient(_targetServer, null, SupportedProtocols, true); - _sslOverTdsStream.FinishHandshake(); - } - catch (AuthenticationException aue) - { - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Authentication exception occurred: {1}", args0: _connectionId, args1: aue?.Message); - return ReportTcpSNIError(aue, SNIError.CertificateValidationErrorCode); - } - catch (InvalidOperationException ioe) + using (TrySNIEventScope.Create(nameof(SNIHandle))) { - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Invalid Operation Exception occurred: {1}", args0: _connectionId, args1: ioe?.Message); - return ReportTcpSNIError(ioe); - } + _validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0; - _stream = _sslStream; - SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, SSL enabled successfully.", args0: _connectionId); - return TdsEnums.SNI_SUCCESS; + try + { + if (_tlsFirst) + { + AuthenticateAsClient(_sslStream, _targetServer, null); + } + else + { + // TODO: Resolve whether to send _serverNameIndication or _targetServer. _serverNameIndication currently results in error. Why? + _sslStream.AuthenticateAsClient(_targetServer, null, s_supportedProtocols, false); + } + if (_sslOverTdsStream is not null) + { + _sslOverTdsStream.FinishHandshake(); + } + } + catch (AuthenticationException aue) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Authentication exception occurred: {1}", args0: _connectionId, args1: aue?.Message); + return ReportTcpSNIError(aue, SNIError.CertificateValidationErrorCode); + } + catch (InvalidOperationException ioe) + { + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.ERR, "Connection Id {0}, Invalid Operation Exception occurred: {1}", args0: _connectionId, args1: ioe?.Message); + return ReportTcpSNIError(ioe); + } + + _stream = _sslStream; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, SSL enabled successfully.", args0: _connectionId); + return TdsEnums.SNI_SUCCESS; + } } /// @@ -608,7 +639,7 @@ public override void DisableSsl() { _sslStream.Dispose(); _sslStream = null; - _sslOverTdsStream.Dispose(); + _sslOverTdsStream?.Dispose(); _sslOverTdsStream = null; _stream = _tcpStream; SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, SSL Disabled. Communication will continue on TCP Stream.", args0: _connectionId); @@ -629,9 +660,18 @@ private bool ValidateServerCertificate(object sender, X509Certificate cert, X509 SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Certificate will not be validated.", args0: _connectionId); return true; } + string serverNameToValidate; + if (!string.IsNullOrEmpty(_hostNameInCertificate)) + { + serverNameToValidate = _hostNameInCertificate; + } + else + { + serverNameToValidate = _targetServer; + } SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Certificate will be validated for Target Server name", args0: _connectionId); - return SNICommon.ValidateSslServerCertificate(_targetServer, cert, policyErrors); + return SNICommon.ValidateSslServerCertificate(serverNameToValidate, cert, policyErrors); } /// @@ -800,14 +840,12 @@ public override void SetAsyncCallbacks(SNIAsyncCallback receiveCallback, SNIAsyn /// Send a packet asynchronously /// /// SNI packet - /// Completion callback /// SNI error code - public override uint SendAsync(SNIPacket packet, SNIAsyncCallback callback = null) + public override uint SendAsync(SNIPacket packet) { using (TrySNIEventScope.Create(nameof(SNITCPHandle))) { - SNIAsyncCallback cb = callback ?? _sendCallback; - packet.WriteToStreamAsync(_stream, cb, SNIProviders.TCP_PROV); + packet.WriteToStreamAsync(_stream, _sendCallback, SNIProviders.TCP_PROV); SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data sent to stream asynchronously", args0: _connectionId); return TdsEnums.SNI_SUCCESS_IO_PENDING; } @@ -822,10 +860,10 @@ public override uint ReceiveAsync(ref SNIPacket packet) { SNIPacket errorPacket; packet = RentPacket(headerSize: 0, dataSize: _bufferSize); - + packet.SetAsyncIOCompletionCallback(_receiveCallback); try { - packet.ReadFromStreamAsync(_stream, _receiveCallback); + packet.ReadFromStreamAsync(_stream); SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNITCPHandle), EventType.INFO, "Connection Id {0}, Data received from stream asynchronously", args0: _connectionId); return TdsEnums.SNI_SUCCESS_IO_PENDING; } @@ -917,8 +955,7 @@ public override void KillConnection() internal static void SetKeepAliveValues(ref Socket socket) { - -#if NETCOREAPP31_AND_ABOVE +#if NETCOREAPP socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 1); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 30); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs index 0147b29f17..e51175059a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SSRP.cs @@ -3,26 +3,38 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Microsoft.Data.SqlClient.SNI { - internal class SSRP + internal sealed class SSRP { private const char SemicolonSeparator = ';'; - private const int SqlServerBrowserPort = 1434; + private const int SqlServerBrowserPort = 1434; //port SQL Server Browser + private const int RecieveMAXTimeoutsForCLNT_BCAST_EX = 15000; //Default max time for response wait + private const int RecieveTimeoutsForCLNT_BCAST_EX = 1000; //subsequent wait time for response after intial wait + private const int ServerResponseHeaderSizeForCLNT_BCAST_EX = 3;//(SVR_RESP + RESP_SIZE) https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/2e1560c9-5097-4023-9f5e-72b9ff1ec3b1 + private const int ValidResponseSizeForCLNT_BCAST_EX = 4096; //valid reponse size should be less than 4096 + private const int FirstTimeoutForCLNT_BCAST_EX = 5000;//wait for first response for 5 seconds + private const int CLNT_BCAST_EX = 2;//request packet /// /// Finds instance port number for given instance name. /// /// SQL Sever Browser hostname /// instance name to find port number + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference /// port number for given instance name - internal static int GetPortByInstanceName(string browserHostName, string instanceName) + internal static int GetPortByInstanceName(string browserHostName, string instanceName, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) { Debug.Assert(!string.IsNullOrWhiteSpace(browserHostName), "browserHostName should not be null, empty, or whitespace"); Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); @@ -32,7 +44,7 @@ internal static int GetPortByInstanceName(string browserHostName, string instanc byte[] responsePacket = null; try { - responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest); + responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, instanceInfoRequest, timerExpire, allIPsInParallel, ipPreference); } catch (SocketException se) { @@ -87,14 +99,17 @@ private static byte[] CreateInstanceInfoRequest(string instanceName) /// /// SQL Sever Browser hostname /// instance name to lookup DAC port + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference /// DAC port for given instance name - internal static int GetDacPortByInstanceName(string browserHostName, string instanceName) + internal static int GetDacPortByInstanceName(string browserHostName, string instanceName, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) { Debug.Assert(!string.IsNullOrWhiteSpace(browserHostName), "browserHostName should not be null, empty, or whitespace"); Debug.Assert(!string.IsNullOrWhiteSpace(instanceName), "instanceName should not be null, empty, or whitespace"); byte[] dacPortInfoRequest = CreateDacPortInfoRequest(instanceName); - byte[] responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, dacPortInfoRequest); + byte[] responsePacket = SendUDPRequest(browserHostName, SqlServerBrowserPort, dacPortInfoRequest, timerExpire, allIPsInParallel, ipPreference); const byte SvrResp = 0x05; const byte ProtocolVersion = 0x01; @@ -131,14 +146,23 @@ private static byte[] CreateDacPortInfoRequest(string instanceName) return requestPacket; } + private class SsrpResult + { + public byte[] ResponsePacket; + public Exception Error; + } + /// /// Sends request to server, and receives response from server by UDP. /// /// UDP server hostname /// UDP server port /// request packet + /// Connection timer expiration + /// query all resolved IP addresses in parallel + /// IP address preference /// response packet from UDP server - private static byte[] SendUDPRequest(string browserHostname, int port, byte[] requestPacket) + private static byte[] SendUDPRequest(string browserHostname, int port, byte[] requestPacket, long timerExpire, bool allIPsInParallel, SqlConnectionIPAddressPreference ipPreference) { using (TrySNIEventScope.Create(nameof(SSRP))) { @@ -146,28 +170,207 @@ private static byte[] SendUDPRequest(string browserHostname, int port, byte[] re Debug.Assert(port >= 0 && port <= 65535, "Invalid port"); Debug.Assert(requestPacket != null && requestPacket.Length > 0, "requestPacket should not be null or 0-length array"); - const int sendTimeOutMs = 1000; - const int receiveTimeOutMs = 1000; + if (IPAddress.TryParse(browserHostname, out IPAddress address)) + { + SsrpResult response = SendUDPRequest(new IPAddress[] { address }, port, requestPacket, allIPsInParallel); + if (response != null && response.ResponsePacket != null) + return response.ResponsePacket; + else if (response != null && response.Error != null) + throw response.Error; + else + return null; + } - IPAddress address = null; - bool isIpAddress = IPAddress.TryParse(browserHostname, out address); + TimeSpan ts = default; + // In case the Timeout is Infinite, we will receive the max value of Int64 as the tick count + // The infinite Timeout is a function of ConnectionString Timeout=0 + if (long.MaxValue != timerExpire) + { + ts = DateTime.FromFileTime(timerExpire) - DateTime.Now; + ts = ts.Ticks < 0 ? TimeSpan.FromTicks(0) : ts; + } - byte[] responsePacket = null; - using (UdpClient client = new UdpClient(!isIpAddress ? AddressFamily.InterNetwork : address.AddressFamily)) + IPAddress[] ipAddresses = SNICommon.GetDnsIpAddresses(browserHostname); + Debug.Assert(ipAddresses.Length > 0, "DNS should throw if zero addresses resolve"); + + switch (ipPreference) + { + case SqlConnectionIPAddressPreference.IPv4First: + { + SsrpResult response4 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetwork).ToArray(), port, requestPacket, allIPsInParallel); + if (response4 != null && response4.ResponsePacket != null) + return response4.ResponsePacket; + + SsrpResult response6 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetworkV6).ToArray(), port, requestPacket, allIPsInParallel); + if (response6 != null && response6.ResponsePacket != null) + return response6.ResponsePacket; + + // No responses so throw first error + if (response4 != null && response4.Error != null) + throw response4.Error; + else if (response6 != null && response6.Error != null) + throw response6.Error; + + break; + } + case SqlConnectionIPAddressPreference.IPv6First: + { + SsrpResult response6 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetworkV6).ToArray(), port, requestPacket, allIPsInParallel); + if (response6 != null && response6.ResponsePacket != null) + return response6.ResponsePacket; + + SsrpResult response4 = SendUDPRequest(ipAddresses.Where(i => i.AddressFamily == AddressFamily.InterNetwork).ToArray(), port, requestPacket, allIPsInParallel); + if (response4 != null && response4.ResponsePacket != null) + return response4.ResponsePacket; + + // No responses so throw first error + if (response6 != null && response6.Error != null) + throw response6.Error; + else if (response4 != null && response4.Error != null) + throw response4.Error; + + break; + } + default: + { + SsrpResult response = SendUDPRequest(ipAddresses, port, requestPacket, true); // allIPsInParallel); + if (response != null && response.ResponsePacket != null) + return response.ResponsePacket; + else if (response != null && response.Error != null) + throw response.Error; + + break; + } + } + + return null; + } + } + + /// + /// Sends request to server, and receives response from server by UDP. + /// + /// IP Addresses + /// UDP server port + /// request packet + /// query all resolved IP addresses in parallel + /// response packet from UDP server + private static SsrpResult SendUDPRequest(IPAddress[] ipAddresses, int port, byte[] requestPacket, bool allIPsInParallel) + { + if (ipAddresses.Length == 0) + return null; + + if (allIPsInParallel) // Used for MultiSubnetFailover + { + List> tasks = new(ipAddresses.Length); + CancellationTokenSource cts = new CancellationTokenSource(); + for (int i = 0; i < ipAddresses.Length; i++) + { + IPEndPoint endPoint = new IPEndPoint(ipAddresses[i], port); + tasks.Add(Task.Factory.StartNew(() => SendUDPRequest(endPoint, requestPacket), cts.Token)); + } + + List> completedTasks = new(); + while (tasks.Count > 0) + { + int first = Task.WaitAny(tasks.ToArray()); + if (tasks[first].Result.ResponsePacket != null) + { + cts.Cancel(); + return tasks[first].Result; + } + else + { + completedTasks.Add(tasks[first]); + tasks.Remove(tasks[first]); + } + } + + Debug.Assert(completedTasks.Count > 0, "completedTasks should never be 0"); + + // All tasks failed. Return the error from the first failure. + return completedTasks[0].Result; + } + else + { + // If not parallel, use the first IP address provided + IPEndPoint endPoint = new IPEndPoint(ipAddresses[0], port); + return SendUDPRequest(endPoint, requestPacket); + } + } + + private static SsrpResult SendUDPRequest(IPEndPoint endPoint, byte[] requestPacket) + { + const int sendTimeOutMs = 1000; + const int receiveTimeOutMs = 1000; + + SsrpResult result = new(); + + try + { + using (UdpClient client = new UdpClient(endPoint.AddressFamily)) { - Task sendTask = client.SendAsync(requestPacket, requestPacket.Length, browserHostname, port); + Task sendTask = client.SendAsync(requestPacket, requestPacket.Length, endPoint); Task receiveTask = null; - + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Waiting for UDP Client to fetch Port info."); if (sendTask.Wait(sendTimeOutMs) && (receiveTask = client.ReceiveAsync()).Wait(receiveTimeOutMs)) { SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Received Port info from UDP Client."); - responsePacket = receiveTask.Result.Buffer; + result.ResponsePacket = receiveTask.Result.Buffer; } } + } + catch (Exception e) + { + result.Error = e; + } - return responsePacket; + return result; + } + + /// + /// Sends request to server, and recieves response from server (SQLBrowser) on port 1434 by UDP + /// Request (https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/a3035afa-c268-4699-b8fd-4f351e5c8e9e) + /// Response (https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/2e1560c9-5097-4023-9f5e-72b9ff1ec3b1) + /// + /// string constaning list of SVR_RESP(just RESP_DATA) + internal static string SendBroadcastUDPRequest() + { + StringBuilder response = new StringBuilder(); + byte[] CLNT_BCAST_EX_Request = new byte[1] { CLNT_BCAST_EX }; //0x02 + // Waits 5 seconds for the first response and every 1 second up to 15 seconds + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/mc-sqlr/f2640a2d-3beb-464b-a443-f635842ebc3e#Appendix_A_3 + int currentTimeOut = FirstTimeoutForCLNT_BCAST_EX; + + using (TrySNIEventScope.Create(nameof(SSRP))) + { + using (UdpClient clientListener = new UdpClient()) + { + Task sendTask = clientListener.SendAsync(CLNT_BCAST_EX_Request, CLNT_BCAST_EX_Request.Length, new IPEndPoint(IPAddress.Broadcast, SqlServerBrowserPort)); + Task receiveTask = null; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Waiting for UDP Client to fetch list of instances."); + Stopwatch sw = new Stopwatch(); //for waiting until 15 sec elapsed + sw.Start(); + try + { + while ((receiveTask = clientListener.ReceiveAsync()).Wait(currentTimeOut) && sw.ElapsedMilliseconds <= RecieveMAXTimeoutsForCLNT_BCAST_EX && receiveTask != null) + { + currentTimeOut = RecieveTimeoutsForCLNT_BCAST_EX; + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SSRP), EventType.INFO, "Received instnace info from UDP Client."); + if (receiveTask.Result.Buffer.Length < ValidResponseSizeForCLNT_BCAST_EX) //discard invalid response + { + response.Append(Encoding.ASCII.GetString(receiveTask.Result.Buffer, ServerResponseHeaderSizeForCLNT_BCAST_EX, receiveTask.Result.Buffer.Length - ServerResponseHeaderSizeForCLNT_BCAST_EX)); //RESP_DATA(VARIABLE) - 3 (RESP_SIZE + SVR_RESP) + } + } + } + finally + { + sw.Stop(); + } + } } + return response.ToString(); } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index aef1ab92b6..6c8140000e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -34,6 +34,7 @@ namespace Microsoft.Data.SqlClient public sealed partial class SqlCommand : DbCommand, ICloneable { private static int _objectTypeCount; // EventSource Counter + private const int MaxRPCNameLength = 1046; internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText; private static readonly Func s_beginExecuteReaderAsync = BeginExecuteReaderAsyncCallback; @@ -113,7 +114,7 @@ protected override void AfterCleared(SqlCommand owner) private static bool _forceInternalEndQuery = false; #endif - private static readonly SqlDiagnosticListener _diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); private bool _parentOperationStarted = false; internal static readonly Action s_cancelIgnoreFailure = CancelIgnoreFailureCallback; @@ -204,10 +205,9 @@ internal bool IsColumnEncryptionEnabled } } - internal bool ShouldUseEnclaveBasedWorkflow - { - get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } - } + internal bool ShouldUseEnclaveBasedWorkflow => + (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) || Connection.AttestationProtocol == SqlConnectionAttestationProtocol.None) && + IsColumnEncryptionEnabled; /// /// Per-command custom providers. It can be provided by the user and can be set more than once. @@ -554,7 +554,7 @@ internal SqlStatistics Statistics if (null != _activeConnection) { if (_activeConnection.StatisticsEnabled || - _diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand)) + s_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand)) { return _activeConnection.Statistics; } @@ -1082,7 +1082,7 @@ public override object ExecuteScalar() // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - using (DiagnosticScope diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction)) + using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) using (TryEventScope.Create("SqlCommand.ExecuteScalar | API | ObjectId {0}", ObjectID)) { SqlStatistics statistics = null; @@ -1162,7 +1162,7 @@ public override int ExecuteNonQuery() // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - using (var diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction)) + using (var diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) using (TryEventScope.Create("SqlCommand.ExecuteNonQuery | API | Object Id {0}", ObjectID)) { SqlStatistics statistics = null; @@ -1670,7 +1670,7 @@ public XmlReader ExecuteXmlReader() // between entry into Execute* API and the thread obtaining the stateObject. _pendingCancel = false; - using (DiagnosticScope diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction)) + using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction)) using (TryEventScope.Create("SqlCommand.ExecuteXmlReader | API | Object Id {0}", ObjectID)) { SqlStatistics statistics = null; @@ -2002,7 +2002,7 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) bool success = false; int? sqlExceptionNumber = null; Exception e = null; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); using (TryEventScope.Create("SqlCommand.ExecuteReader | API | Object Id {0}", ObjectID)) { @@ -2016,10 +2016,9 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) } catch (Exception ex) { - if (ex is SqlException) + if (ex is SqlException sqlException) { - SqlException exception = (SqlException)ex; - sqlExceptionNumber = exception.Number; + sqlExceptionNumber = sqlException.Number; } e = ex; @@ -2031,11 +2030,11 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true); if (e != null) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } else { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); } } } @@ -2130,12 +2129,16 @@ private void CleanupExecuteReaderAsync(Task task, TaskCompletionS Exception e = task.Exception.InnerException; if (!_parentOperationStarted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } source.SetException(e); } else { + if (!_parentOperationStarted) + { + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + } if (task.IsCanceled) { source.SetCanceled(); @@ -2144,10 +2147,6 @@ private void CleanupExecuteReaderAsync(Task task, TaskCompletionS { source.SetResult(task.Result); } - if (!_parentOperationStarted) - { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } } } @@ -2525,7 +2524,7 @@ private Task InternalExecuteNonQueryWithRetryAsync(CancellationToken cancel private Task InternalExecuteNonQueryAsync(CancellationToken cancellationToken) { SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2545,32 +2544,35 @@ private Task InternalExecuteNonQueryAsync(CancellationToken cancellationTok { returnedTask = RegisterForConnectionCloseNotification(returnedTask); - Task.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) => - { - registration.Dispose(); - if (t.IsFaulted) + Task.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null) + .ContinueWith((Task task) => { - Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); - source.SetException(e); - } - else - { - if (t.IsCanceled) + registration.Dispose(); + if (task.IsFaulted) { - source.SetCanceled(); + Exception e = task.Exception.InnerException; + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + source.SetException(e); } else { - source.SetResult(t.Result); + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + if (task.IsCanceled) + { + source.SetCanceled(); + } + else + { + source.SetResult(task.Result); + } } - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } - }, TaskScheduler.Default); + }, + TaskScheduler.Default + ); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } @@ -2629,7 +2631,7 @@ private Task InternalExecuteReaderAsync(CommandBehavior behavior, Guid operationId = default(Guid); if (!_parentOperationStarted) { - operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); } TaskCompletionSource source = new TaskCompletionSource(); @@ -2674,7 +2676,7 @@ private Task InternalExecuteReaderAsync(CommandBehavior behavior, { if (!_parentOperationStarted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); } source.SetException(e); @@ -2701,7 +2703,7 @@ private Task InternalExecuteScalarAsync(CancellationToken cancellationTo SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteScalarAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteScalarAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText); _parentOperationStarted = true; - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) => { @@ -2712,68 +2714,71 @@ private Task InternalExecuteScalarAsync(CancellationToken cancellationTo } else if (executeTask.IsFaulted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException); source.SetException(executeTask.Exception.InnerException); } else { SqlDataReader reader = executeTask.Result; - reader.ReadAsync(cancellationToken).ContinueWith((readTask) => - { - try + reader.ReadAsync(cancellationToken) + .ContinueWith((Task readTask) => { - if (readTask.IsCanceled) - { - reader.Dispose(); - source.SetCanceled(); - } - else if (readTask.IsFaulted) - { - reader.Dispose(); - _diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException); - source.SetException(readTask.Exception.InnerException); - } - else + try { - Exception exception = null; - object result = null; - try - { - bool more = readTask.Result; - if (more && reader.FieldCount > 0) - { - try - { - result = reader.GetValue(0); - } - catch (Exception e) - { - exception = e; - } - } - } - finally + if (readTask.IsCanceled) { reader.Dispose(); + source.SetCanceled(); } - if (exception != null) + else if (readTask.IsFaulted) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, exception); - source.SetException(exception); + reader.Dispose(); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException); + source.SetException(readTask.Exception.InnerException); } else { - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - source.SetResult(result); + Exception exception = null; + object result = null; + try + { + bool more = readTask.Result; + if (more && reader.FieldCount > 0) + { + try + { + result = reader.GetValue(0); + } + catch (Exception e) + { + exception = e; + } + } + } + finally + { + reader.Dispose(); + } + if (exception != null) + { + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, exception); + source.SetException(exception); + } + else + { + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + source.SetResult(result); + } } } - } - catch (Exception e) - { - // exception thrown by Dispose... - source.SetException(e); - } - }, TaskScheduler.Default); + catch (Exception e) + { + // exception thrown by Dispose... + source.SetException(e); + } + }, + TaskScheduler.Default + ); } _parentOperationStarted = false; return source.Task; @@ -2798,7 +2803,7 @@ private Task InternalExecuteXmlReaderWithRetryAsync(CancellationToken private Task InternalExecuteXmlReaderAsync(CancellationToken cancellationToken) { SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText); - Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction); + Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction); TaskCompletionSource source = new TaskCompletionSource(); @@ -2818,32 +2823,36 @@ private Task InternalExecuteXmlReaderAsync(CancellationToken cancella { returnedTask = RegisterForConnectionCloseNotification(returnedTask); - Task.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null).ContinueWith((t) => - { - registration.Dispose(); - if (t.IsFaulted) - { - Exception e = t.Exception.InnerException; - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); - source.SetException(e); - } - else + Task.Factory.FromAsync(BeginExecuteXmlReaderAsync, EndExecuteXmlReaderAsync, null) + .ContinueWith((Task task) => { - if (t.IsCanceled) + registration.Dispose(); + if (task.IsFaulted) { - source.SetCanceled(); + Exception e = task.Exception.InnerException; + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + source.SetException(e); } else { - source.SetResult(t.Result); + s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction); + if (task.IsCanceled) + { + source.SetCanceled(); + } + else + { + source.SetResult(task.Result); + } + } - _diagnosticListener.WriteCommandAfter(operationId, this, _transaction); - } - }, TaskScheduler.Default); + }, + TaskScheduler.Default + ); } catch (Exception e) { - _diagnosticListener.WriteCommandError(operationId, this, _transaction, e); + s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e); source.SetException(e); } @@ -4211,7 +4220,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (isRequestedByEnclave) { - if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl) && Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(this._activeConnection.Parser.EnclaveType); } @@ -4636,8 +4645,11 @@ private void GenerateEnclavePackage() return; } - if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl) && + Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) + { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(this._activeConnection.Parser.EnclaveType); + } string enclaveType = this._activeConnection.Parser.EnclaveType; if (string.IsNullOrWhiteSpace(enclaveType)) @@ -5802,7 +5814,20 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql GetRPCObject(0, userParameterCount, ref rpc); rpc.ProcID = 0; - rpc.rpcName = this.CommandText; // just get the raw command text + + // TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName + // 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523 + // each char takes 2 bytes. 523 * 2 = 1046 + int commandTextLength = ADP.CharSize * CommandText.Length; + + if (commandTextLength <= MaxRPCNameLength) + { + rpc.rpcName = CommandText; // just get the raw command text + } + else + { + throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength); + } SetUpRPCParameters(rpc, inSchema, parameters); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs index bcb9378643..660b5934e0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -21,7 +21,7 @@ using System.Threading.Tasks; using Microsoft.Data.Common; using Microsoft.Data.ProviderBase; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient { @@ -412,9 +412,15 @@ private void CacheConnectionStringProperties() if (connString != null) { _connectRetryCount = connString.ConnectRetryCount; + // For Azure Synapse ondemand connections, set _connectRetryCount to 5 instead of 1 to greatly improve recovery + // success rate. Note: Synapse should be detected first as it could be detected as a regular Azure SQL DB endpoint. + if (_connectRetryCount == 1 && ADP.IsAzureSynapseOnDemandEndpoint(connString.DataSource)) + { + _connectRetryCount = 5; + } // For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery - // success rate - if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) + // success rate + else if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) { _connectRetryCount = 2; } @@ -2341,7 +2347,7 @@ internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnD MemoryStream stm = new MemoryStream((byte[])value); - o = SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); + o = Server.SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); Debug.Assert(o != null, "object could NOT be created"); return o; @@ -2369,7 +2375,7 @@ internal byte[] GetBytes(object o, out Format format, out int maxSize) using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) { - SerializationHelperSql9.Serialize(stm, o); + Server.SerializationHelperSql9.Serialize(stm, o); retval = stm.ToArray(); } return retval; diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs deleted file mode 100644 index 967cf6acf8..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlError.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed class SqlError - { - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, uint win32ErrorCode, Exception exception = null) - : this(infoNumber, errorState, errorClass, server, errorMessage, procedure, lineNumber, exception) - { - Win32ErrorCode = (int)win32ErrorCode; - } - - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, Exception exception = null) - { - Number = infoNumber; - State = errorState; - Class = errorClass; - Server = server; - Message = errorMessage; - Procedure = procedure; - LineNumber = lineNumber; - Win32ErrorCode = 0; - Exception = exception; - if (errorClass != 0) - { - SqlClientEventSource.Log.TryTraceEvent("SqlError.ctor | ERR | Info Number {0}, Error State {1}, Error Class {2}, Error Message '{3}', Procedure '{4}', Line Number {5}", infoNumber, (int)errorState, (int)errorClass, errorMessage, procedure ?? "None", (int)lineNumber); - } - } - - /// - // There is no exception stack included because the correct exception stack is only available - // on SqlException, and to obtain that the SqlError would have to have backpointers all the - // way back to SqlException. If the user needs a call stack, they can obtain it on SqlException. - public override string ToString() - { - return typeof(SqlError).ToString() + ": " + Message; // since this is sealed so we can change GetType to typeof - } - - /// - public string Source { get; private set; } = TdsEnums.SQL_PROVIDER_NAME; - - /// - public int Number { get; private set; } - - /// - public byte State { get; private set; } - - /// - public byte Class { get; private set; } - - /// - public string Server { get; private set; } - - /// - public string Message { get; private set; } - - /// - public string Procedure { get; private set; } - - /// - public int LineNumber { get; private set; } - - internal int Win32ErrorCode { get; private set; } - - internal Exception Exception { get; private set; } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index b863db3cba..20eb221f96 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -370,7 +370,7 @@ private void EnlistNonNull(Transaction tx) // NOTE: Global Transactions is an Azure SQL DB only // feature where the Transaction Manager (TM) is not // MS-DTC. Sys.Tx added APIs to support Non MS-DTC - // promoter types/TM in .NET 4.6.1. Following directions + // promoter types/TM in .NET 4.6.2. Following directions // from .NETFX shiproom, to avoid a "hard-dependency" // (compile time) on Sys.Tx, we use reflection to invoke // the new APIs. Further, the _isGlobalTransaction flag diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index dcbc62ef0d..83bf554c2c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -103,6 +103,9 @@ public void AssertUnrecoverableStateCountIsCorrect() internal sealed class SqlInternalConnectionTds : SqlInternalConnection, IDisposable { + // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 + internal const int MsalHttpRetryStatusCode = 429; + // CONNECTION AND STATE VARIABLES private readonly SqlConnectionPoolGroupProviderInfo _poolGroupProviderInfo; // will only be null when called for ChangePassword, or creating SSE User Instance private TdsParser _parser; @@ -1242,7 +1245,7 @@ private void CompleteLogin(bool enlistOK) _parser._physicalStateObj.SniContext = SniContext.Snix_Login; } - private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword) + private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword, SqlConnectionEncryptOption encrypt) { // create a new login record SqlLogin login = new SqlLogin(); @@ -1348,7 +1351,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The SQLDNSCaching feature is implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; - _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData); + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, encrypt); } private void LoginFailure() @@ -1549,7 +1552,7 @@ private void LoginNoFailover(ServerInfo serverInfo, throw SQL.ROR_TimeoutAfterRoutingInfo(this); } - serverInfo = new ServerInfo(ConnectionOptions, RoutingInfo, serverInfo.ResolvedServerName); + serverInfo = new ServerInfo(ConnectionOptions, RoutingInfo, serverInfo.ResolvedServerName, serverInfo.ServerSPN); _timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = serverInfo.UserServerName; @@ -1693,7 +1696,7 @@ TimeoutTimer timeout int sleepInterval = 100; //milliseconds to sleep (back off) between attempts. long timeoutUnitInterval; - ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost); + ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost, connectionOptions.FailoverPartnerSPN); ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); if (null == ServerProvidedFailOverPartner) @@ -1853,11 +1856,36 @@ private void ResolveExtendedServerName(ServerInfo serverInfo, bool aliasLookup, string host = serverInfo.UserServerName; string protocol = serverInfo.UserProtocol; - //TODO: fix local host enforcement with datadirectory and failover - if (options.EnforceLocalHost) - { - // verify LocalHost for |DataDirectory| usage - SqlConnectionString.VerifyLocalHostAndFixup(ref host, true, true /*fix-up to "."*/); + if (aliasLookup) + { // We skip this for UserInstances... + // Perform registry lookup to see if host is an alias. It will appropriately set host and protocol, if an Alias. + // Check if it was already resolved, during CR reconnection _currentSessionData values will be copied from + // _reconnectSessonData of the previous connection + if (_currentSessionData != null && !string.IsNullOrEmpty(host)) + { + Tuple hostPortPair; + if (_currentSessionData._resolvedAliases.TryGetValue(host, out hostPortPair)) + { + host = hostPortPair.Item1; + protocol = hostPortPair.Item2; + } + else + { + TdsParserStaticMethods.AliasRegistryLookup(ref host, ref protocol); + _currentSessionData._resolvedAliases.Add(serverInfo.UserServerName, new Tuple(host, protocol)); + } + } + else + { + TdsParserStaticMethods.AliasRegistryLookup(ref host, ref protocol); + } + + //TODO: fix local host enforcement with datadirectory and failover + if (options.EnforceLocalHost) + { + // verify LocalHost for |DataDirectory| usage + SqlConnectionString.VerifyLocalHostAndFixup(ref host, true, true /*fix-up to "."*/); + } } serverInfo.SetDerivedNames(protocol, host); @@ -1882,17 +1910,14 @@ private void AttemptOneLogin( this, ignoreSniOpenTimeout, timeout.LegacyTimerExpire, - ConnectionOptions.Encrypt, - ConnectionOptions.TrustServerCertificate, - ConnectionOptions.IntegratedSecurity, - withFailover, - ConnectionOptions.Authentication); + ConnectionOptions, + withFailover); _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); _parser._physicalStateObj.SniContext = SniContext.Snix_Login; - this.Login(serverInfo, timeout, newPassword, newSecurePassword); + this.Login(serverInfo, timeout, newPassword, newSecurePassword, ConnectionOptions.Encrypt); _timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth); _timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.PostLogin); @@ -1994,36 +2019,36 @@ internal bool IgnoreEnvChange internal void OnEnvChange(SqlEnvChange rec) { Debug.Assert(!IgnoreEnvChange, "This function should not be called if IgnoreEnvChange is set!"); - switch (rec.type) + switch (rec._type) { case TdsEnums.ENV_DATABASE: // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalDatabase = rec.newValue; + _originalDatabase = rec._newValue; } - CurrentDatabase = rec.newValue; + CurrentDatabase = rec._newValue; break; case TdsEnums.ENV_LANG: // If connection is not open and recovery is not in progress, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalLanguage = rec.newValue; + _originalLanguage = rec._newValue; } - _currentLanguage = rec.newValue; + _currentLanguage = rec._newValue; break; case TdsEnums.ENV_PACKETSIZE: - _currentPacketSize = int.Parse(rec.newValue, CultureInfo.InvariantCulture); + _currentPacketSize = int.Parse(rec._newValue, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COLLATION: if (_currentSessionData != null) { - _currentSessionData._collation = rec.newCollation; + _currentSessionData._collation = rec._newCollation; } break; @@ -2043,20 +2068,20 @@ internal void OnEnvChange(SqlEnvChange rec) { throw SQL.ROR_FailoverNotSupportedServer(this); } - _currentFailoverPartner = rec.newValue; + _currentFailoverPartner = rec._newValue; break; case TdsEnums.ENV_PROMOTETRANSACTION: - byte[] dtcToken = null; - if (rec.newBinRented) + byte[] dtcToken; + if (rec._newBinRented) { - dtcToken = new byte[rec.newLength]; - Buffer.BlockCopy(rec.newBinValue, 0, dtcToken, 0, dtcToken.Length); + dtcToken = new byte[rec._newLength]; + Buffer.BlockCopy(rec._newBinValue, 0, dtcToken, 0, dtcToken.Length); } else { - dtcToken = rec.newBinValue; - rec.newBinValue = null; + dtcToken = rec._newBinValue; + rec._newBinValue = null; } PromotedDTCToken = dtcToken; break; @@ -2077,16 +2102,16 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_USERINSTANCE: - _instanceName = rec.newValue; + _instanceName = rec._newValue; break; case TdsEnums.ENV_ROUTING: SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received routing info", ObjectID); - if (string.IsNullOrEmpty(rec.newRoutingInfo.ServerName) || rec.newRoutingInfo.Protocol != 0 || rec.newRoutingInfo.Port == 0) + if (string.IsNullOrEmpty(rec._newRoutingInfo.ServerName) || rec._newRoutingInfo.Protocol != 0 || rec._newRoutingInfo.Port == 0) { throw SQL.ROR_InvalidRoutingInfo(this); } - RoutingInfo = rec.newRoutingInfo; + RoutingInfo = rec._newRoutingInfo; break; default: @@ -2421,7 +2446,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Deal with Msal service exceptions first, retry if 429 received. catch (MsalServiceException serviceException) { - if (429 == serviceException.StatusCode) + if (serviceException.StatusCode == MsalHttpRetryStatusCode) { RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter; if (retryAfter.Delta.HasValue) @@ -2440,9 +2465,15 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - break; + SqlClientEventSource.Log.TryTraceEvent(" Timeout: {0}", serviceException.ErrorCode); + throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), serviceException.ErrorCode, serviceException); } } + else + { + SqlClientEventSource.Log.TryTraceEvent(" {0}", serviceException.ErrorCode); + throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username); + } } // Deal with normal MsalExceptions. catch (MsalException msalException) @@ -2453,21 +2484,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { SqlClientEventSource.Log.TryTraceEvent(" {0}", msalException.ErrorCode); - // Error[0] - SqlErrorCollection sqlErs = new(); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - - // Error[1] - string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - - // Error[2] - if (!string.IsNullOrEmpty(msalException.Message)) - { - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - } - SqlException exc = SqlException.CreateException(sqlErs, "", this); - throw exc; + throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username); } SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); @@ -2776,6 +2793,7 @@ internal sealed class ServerInfo internal string ResolvedServerName { get; private set; } // the resolved servername only internal string ResolvedDatabaseName { get; private set; } // name of target database after resolution internal string UserProtocol { get; private set; } // the user specified protocol + internal string ServerSPN { get; private set; } // the server SPN // The original user-supplied server name from the connection string. // If connection string has no Data Source, the value is set to string.Empty. @@ -2796,10 +2814,16 @@ private set internal readonly string PreRoutingServerName; // Initialize server info from connection options, - internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource) { } + internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource, userOptions.ServerSPN) { } + + // Initialize server info from connection options, but override DataSource and ServerSPN with given server name and server SPN + internal ServerInfo(SqlConnectionString userOptions, string serverName, string serverSPN) : this(userOptions, serverName) + { + ServerSPN = serverSPN; + } // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, string serverName) + private ServerInfo(SqlConnectionString userOptions, string serverName) { //----------------- // Preconditions @@ -2818,7 +2842,7 @@ internal ServerInfo(SqlConnectionString userOptions, string serverName) // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName) + internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName, string serverSPN) { //----------------- // Preconditions @@ -2839,6 +2863,7 @@ internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string UserProtocol = TdsEnums.TCP; SetDerivedNames(UserProtocol, UserServerName); ResolvedDatabaseName = userOptions.InitialCatalog; + ServerSPN = serverSPN; } internal void SetDerivedNames(string protocol, string serverName) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 2eecfdc8cc..1c3df017dd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -4,136 +4,22 @@ using System; using System.ComponentModel; -using System.Data; using System.Data.Common; -using System.Diagnostics; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { /// - public sealed class SqlTransaction : DbTransaction + public sealed partial class SqlTransaction : DbTransaction { - private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { - _isolationLevel = iso; - _connection = con; - - if (internalTransaction == null) - { - _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); - } - else - { - Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); - _internalTransaction = internalTransaction; - _internalTransaction.InitParent(this); - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PROPERTIES - //////////////////////////////////////////////////////////////////////////////////////// - - /// - new public SqlConnection Connection - { - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - override protected DbConnection DbConnection - { - get - { - return Connection; - } - } - - internal SqlInternalTransaction InternalTransaction - { - get - { - return _internalTransaction; - } - } - - /// - override public IsolationLevel IsolationLevel - { - get - { - ZombieCheck(); - return _isolationLevel; - } - } - - private bool Is2005PartialZombie - { - get - { - return (null != _internalTransaction && _internalTransaction.IsCompleted); - } - } - - internal bool IsZombied - { - get - { - return (null == _internalTransaction || _internalTransaction.IsCompleted); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal SqlStatistics Statistics - { - get - { - if (null != _connection) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } + private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); //////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// /// - override public void Commit() + public override void Commit() { using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) { @@ -191,7 +77,7 @@ protected override void Dispose(bool disposing) } /// - override public void Rollback() + public override void Rollback() { using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) { @@ -284,45 +170,5 @@ public void Save(string savePointName) } } } - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - internal void Zombie() - { - // For 2005, we have to defer "zombification" until - // we get past the users' next rollback, else we'll - // throw an exception there that is a breaking change. - // Of course, if the connection is already closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - if (null != internalConnection && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} 2005 deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre-2005 zombification - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PRIVATE METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - private void ZombieCheck() - { - // If this transaction has been completed, throw exception since it is unusable. - if (IsZombied) - { - if (Is2005PartialZombie) - { - _internalTransaction = null; // 2005 zombification - } - - throw ADP.TransactionZombied(this); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs index 8e9039a6aa..4aebe4b518 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -185,7 +185,7 @@ internal static void ContinueTaskWithState(Task task, completion.SetException(e); } } - }, + }, state: state, scheduler: TaskScheduler.Default ); @@ -228,7 +228,7 @@ internal static void SetTimeoutException(TaskCompletionSource completion } } - internal static void SetTimeoutExceptionWithState(TaskCompletionSource completion, int timeout, object state, Func onFailure, CancellationToken cancellationToken) + internal static void SetTimeoutExceptionWithState(TaskCompletionSource completion, int timeout, object state, Func onFailure, CancellationToken cancellationToken) { if (timeout > 0) { @@ -510,6 +510,10 @@ internal static Exception ActiveDirectoryDeviceFlowTimeout() return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication); } + internal static Exception ActiveDirectoryTokenRetrievingTimeout(string authenticaton, string errorCode, Exception exception) + { + return ADP.TimeoutException(StringsHelper.GetString(Strings.AAD_Token_Retrieving_Timeout, authenticaton, errorCode, exception?.Message), exception); + } // // SQL.DataCommand @@ -1643,6 +1647,21 @@ internal static Exception AttestationInfoNotReturnedFromSqlServer(string enclave { return ADP.Argument(StringsHelper.GetString(Strings.TCE_AttestationInfoNotReturnedFromSQLServer, enclaveType, enclaveAttestationUrl)); } + + internal static SqlException AttestationFailed(string errorMessage, Exception innerException = null) + { + SqlErrorCollection errors = new(); + errors.Add(new SqlError( + infoNumber: 0, + errorState: 0, + errorClass: 0, + server: null, + errorMessage, + procedure: string.Empty, + lineNumber: 0)); + return SqlException.CreateException(errors, serverVersion: string.Empty, Guid.Empty, innerException); + } + #endregion Always Encrypted - Errors when performing attestation #region Always Encrypted - Errors when establishing secure channel diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 6b77e98c2e..36ecb2cd72 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -117,6 +117,8 @@ internal sealed partial class TdsParser private bool _is2012 = false; + private bool _is2022 = false; + private byte[][] _sniSpnBuffer = null; // SqlStatistics @@ -171,7 +173,7 @@ internal sealed partial class TdsParser internal string EnclaveType { get; set; } internal bool isTcpProtocol { get; set; } - internal string FQDNforDNSCahce { get; set; } + internal string FQDNforDNSCache { get; set; } /// /// Get if data classification is enabled by the server. @@ -359,12 +361,15 @@ internal void Connect( SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, - bool encrypt, - bool trustServerCert, - bool integratedSecurity, - bool withFailover, - SqlAuthenticationMethod authType) + SqlConnectionString connectionOptions, + bool withFailover) { + SqlConnectionEncryptOption encrypt = connectionOptions.Encrypt; + bool trustServerCert = connectionOptions.TrustServerCertificate; + bool integratedSecurity = connectionOptions.IntegratedSecurity; + SqlAuthenticationMethod authType = connectionOptions.Authentication; + string hostNameInCertificate = connectionOptions.HostNameInCertificate; + if (_state != TdsParserState.Closed) { Debug.Fail("TdsParser.Connect called on non-closed connection!"); @@ -393,10 +398,10 @@ internal void Connect( authType == SqlAuthenticationMethod.NotSpecified ? SqlAuthenticationMethod.SqlPassword.ToString() : authType.ToString()); } - // Encryption is not supported on SQL Local DB - disable it for current session. - if (connHandler.ConnectionOptions.LocalDBInstance != null && encrypt) + // Encryption is not supported on SQL Local DB - disable it if they have only specified Mandatory + if (connHandler.ConnectionOptions.LocalDBInstance != null && encrypt == SqlConnectionEncryptOption.Mandatory) { - encrypt = false; + encrypt = SqlConnectionEncryptOption.Optional; SqlClientEventSource.Log.TryTraceEvent(" Encryption will be disabled as target server is a SQL Local DB instance."); } @@ -409,6 +414,12 @@ internal void Connect( SqlClientEventSource.Log.TryTraceEvent("TdsParser.Connect | SEC | SSPI or Active Directory Authentication Library loaded for SQL Server based integrated authentication"); } + // if Strict encryption is chosen trust server certificate should always be false. + if (encrypt == SqlConnectionEncryptOption.Strict) + { + trustServerCert = false; + } + byte[] instanceName = null; Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); @@ -417,19 +428,21 @@ internal void Connect( bool fParallel = _connHandler.ConnectionOptions.MultiSubnetFailover; - FQDNforDNSCahce = serverInfo.ResolvedServerName; + FQDNforDNSCache = serverInfo.ResolvedServerName; - int commaPos = FQDNforDNSCahce.IndexOf(","); + int commaPos = FQDNforDNSCache.IndexOf(","); if (commaPos != -1) { - FQDNforDNSCahce = FQDNforDNSCahce.Substring(0, commaPos); + FQDNforDNSCache = FQDNforDNSCache.Substring(0, commaPos); } _connHandler.pendingSQLDNSObject = null; // AD Integrated behaves like Windows integrated when connecting to a non-fedAuth server - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, false, true, fParallel, - _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, + false, true, fParallel, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject, serverInfo.ServerSPN, + integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated, encrypt == SqlConnectionEncryptOption.Strict, + hostNameInCertificate); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -472,13 +485,13 @@ internal void Connect( if (null == _connHandler.pendingSQLDNSObject) { // for DNS Caching phase 1 - _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); } if (!ClientOSEncryptionSupport) { //If encryption is required, an error will be thrown. - if (encrypt) + if (encrypt != SqlConnectionEncryptOption.Optional) { _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); @@ -488,14 +501,15 @@ internal void Connect( } SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); - SendPreLoginHandshake(instanceName, encrypt); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); _physicalStateObj.SniContext = SniContext.Snix_PreLogin; SqlClientEventSource.Log.TryTraceEvent(" Consuming prelogin handshake"); - PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); + PreLoginHandshakeStatus status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, + out _connHandler._fedAuthRequired, encrypt == SqlConnectionEncryptOption.Strict); if (status == PreLoginHandshakeStatus.InstanceFailure) { @@ -506,7 +520,7 @@ internal void Connect( _physicalStateObj.SniContext = SniContext.Snix_Connect; _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, ref _sniSpnBuffer, true, true, fParallel, - _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject, integratedSecurity); + _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject, serverInfo.ServerSPN, integratedSecurity); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -523,11 +537,12 @@ internal void Connect( if (null == _connHandler.pendingSQLDNSObject) { // for DNS Caching phase 1 - _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce, ref _connHandler.pendingSQLDNSObject); + _physicalStateObj.AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache, ref _connHandler.pendingSQLDNSObject); } - SendPreLoginHandshake(instanceName, encrypt); - status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity); + status = ConsumePreLoginHandshake(encrypt, trustServerCert, integratedSecurity, out marsCapable, out _connHandler._fedAuthRequired, + encrypt == SqlConnectionEncryptOption.Strict); // Don't need to check for 7.0 failure, since we've already consumed // one pre-login packet and know we are connecting to 2000. @@ -643,9 +658,23 @@ internal void PutSession(TdsParserStateObject session) } } - - private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) + private void SendPreLoginHandshake( + byte[] instanceName, + SqlConnectionEncryptOption encrypt, + bool integratedSecurity) { + if (encrypt == SqlConnectionEncryptOption.Strict) + { + //Always validate the certificate when in strict encryption mode + uint info = TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE | TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE | TdsEnums.SNI_SSL_SEND_ALPN_EXTENSION; + + EnableSsl(info, encrypt, integratedSecurity); + + // Since encryption has already been negotiated, we need to set encryption not supported in + // prelogin so that we don't try to negotiate encryption again during ConsumePreLoginHandshake. + _encryptionOption = EncryptionOptions.NOT_SUP; + } + // PreLoginHandshake buffer consists of: // 1) Standard header, with type = MT_PRELOGIN // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length) @@ -703,7 +732,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) else { // Else, inform server of user request. - if (encrypt) + if (encrypt == SqlConnectionEncryptOption.Mandatory) { payload[payloadLength] = (byte)EncryptionOptions.ON; _encryptionOption = EncryptionOptions.ON; @@ -800,7 +829,56 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt) _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); } - private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trustServerCert, bool integratedSecurity, out bool marsCapable, out bool fedAuthRequired) + private void EnableSsl(uint info, SqlConnectionEncryptOption encrypt, bool integratedSecurity) + { + uint error = 0; + + if (encrypt && !integratedSecurity) + { + // optimization: in case of SQL Authentication and encryption in TDS, set SNI_SSL_IGNORE_CHANNEL_BINDINGS + // to let SNI know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. + // This applies to Native SNI + info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; + } + + error = _physicalStateObj.EnableSsl(ref info, encrypt == SqlConnectionEncryptOption.Strict); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + int protocolVersion = 0; + WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); + + SslProtocols protocol = (SslProtocols)protocolVersion; + string warningMessage = protocol.GetProtocolWarning(); + if (!string.IsNullOrEmpty(warningMessage)) + { + if (!encrypt && LocalAppContextSwitches.SuppressInsecureTLSWarning) + { + // Skip console warning + SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(EnableSsl), SqlClientLogger.LogLevel.Warning, warningMessage); + } + else + { + // This logs console warning of insecure protocol in use. + _logger.LogWarning(nameof(TdsParser), nameof(EnableSsl), warningMessage); + } + } + + // create a new packet encryption changes the internal packet size + _physicalStateObj.ClearAllWritePackets(); + } + + private PreLoginHandshakeStatus ConsumePreLoginHandshake( + SqlConnectionEncryptOption encrypt, + bool trustServerCert, + bool integratedSecurity, + out bool marsCapable, + out bool fedAuthRequired, + bool tlsFirst) { marsCapable = _fMARS; // Assign default value fedAuthRequired = false; @@ -894,9 +972,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus _physicalStateObj.Dispose(); ThrowExceptionAndWarning(_physicalStateObj); } - break; - case (EncryptionOptions.OFF): if (serverOption == EncryptionOptions.OFF) { @@ -912,7 +988,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus break; case (EncryptionOptions.NOT_SUP): - if (serverOption == EncryptionOptions.REQ) + if (!tlsFirst && serverOption == EncryptionOptions.REQ) { _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); @@ -920,7 +996,6 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus } break; - default: Debug.Fail("Invalid client encryption option detected"); break; @@ -929,50 +1004,13 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(bool encrypt, bool trus if (_encryptionOption == EncryptionOptions.ON || _encryptionOption == EncryptionOptions.LOGIN) { - uint error = 0; - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. - bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || (_connHandler._accessTokenInBytes != null && !trustServerCert); + bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || + (_connHandler._accessTokenInBytes != null && !trustServerCert); uint info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (is2005OrLater ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - if (encrypt && !integratedSecurity) - { - // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI - // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. - // This applies to Native SNI - info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; - } - - error = _physicalStateObj.EnableSsl(ref info); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - int protocolVersion = 0; - WaitForSSLHandShakeToComplete(ref error, ref protocolVersion); - - SslProtocols protocol = (SslProtocols)protocolVersion; - string warningMessage = protocol.GetProtocolWarning(); - if (!string.IsNullOrEmpty(warningMessage)) - { - if (!encrypt && LocalAppContextSwitches.SuppressInsecureTLSWarning) - { - // Skip console warning - SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(ConsumePreLoginHandshake), SqlClientLogger.LogLevel.Warning, warningMessage); - } - else - { - // This logs console warning of insecure protocol in use. - _logger.LogWarning(nameof(TdsParser), nameof(ConsumePreLoginHandshake), warningMessage); - } - } - - // create a new packet encryption changes the internal packet size - _physicalStateObj.ClearAllWritePackets(); + EnableSsl(info, encrypt, integratedSecurity); } break; @@ -2179,7 +2217,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead { if (!this.Connection.IgnoreEnvChange) { - switch (env.type) + switch (env._type) { case TdsEnums.ENV_BEGINTRAN: case TdsEnums.ENV_ENLISTDTC: @@ -2194,12 +2232,12 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead if (null != _currentTransaction) { - _currentTransaction.TransactionId = env.newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. + _currentTransaction.TransactionId = env._newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. } else { - TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env.type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; - _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env.newLongValue); + TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env._type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; + _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env._newLongValue); } if (null != _statistics && !_statisticsIsInTransaction) { @@ -2224,22 +2262,22 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead // Check null for case where Begin and Rollback obtained in the same message. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { - Debug.Assert(_currentTransaction.TransactionId != env.newLongValue, "transaction id's are not equal!"); + Debug.Assert(_currentTransaction.TransactionId != env._newLongValue, "transaction id's are not equal!"); } #endif - if (TdsEnums.ENV_COMMITTRAN == env.type) + if (TdsEnums.ENV_COMMITTRAN == env._type) { _currentTransaction.Completed(TransactionState.Committed); } - else if (TdsEnums.ENV_ROLLBACKTRAN == env.type) + else if (TdsEnums.ENV_ROLLBACKTRAN == env._type) { // Hold onto transaction id if distributed tran is rolled back. This must // be sent to the server on subsequent executions even though the transaction // is considered to be rolled back. if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) { - _retainedTransactionId = env.oldLongValue; + _retainedTransactionId = env._oldLongValue; } _currentTransaction.Completed(TransactionState.Aborted); } @@ -2258,7 +2296,7 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead } } SqlEnvChange head = env; - env = env.Next; + env = env._next; head.Clear(); head = null; } @@ -2556,7 +2594,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, while (tokenLength > processedLength) { var env = new SqlEnvChange(); - if (!stateObj.TryReadByte(out env.type)) + if (!stateObj.TryReadByte(out env._type)) { return false; } @@ -2568,11 +2606,11 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, } else { - tail.Next = env; + tail._next = env; tail = env; } - switch (env.type) + switch (env._type) { case TdsEnums.ENV_DATABASE: case TdsEnums.ENV_LANG: @@ -2589,16 +2627,16 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) + if (env._newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) { _defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE; _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } else { - Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); + Debug.Assert(env._newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); - string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); + string stringCodePage = env._newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); _defaultCodePage = int.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture); _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); @@ -2614,9 +2652,10 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // Changing packet size does not support retry, should not pend" throw SQL.SynchronousCallMayNotPend(); } + // Only set on physical state object - this should only occur on LoginAck prior // to MARS initialization! - int packetSize = int.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + int packetSize = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); if (_physicalStateObj.SetPacketSize(packetSize)) { @@ -2626,7 +2665,6 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // Update SNI ConsumerInfo value to be resulting packet size uint unsignedPacketSize = (uint)packetSize; - uint result = _physicalStateObj.SetConnectionBufferSize(ref unsignedPacketSize); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo"); } @@ -2638,7 +2676,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - _defaultLCID = int.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + _defaultLCID = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COMPFLAGS: @@ -2649,54 +2687,54 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, break; case TdsEnums.ENV_COLLATION: - Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!"); + Debug.Assert(env._newLength == 5 || env._newLength == 0, "Improper length in new collation!"); if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.newLength = byteLength; - if (env.newLength == 5) + env._newLength = byteLength; + if (env._newLength == 5) { - if (!TryProcessCollation(stateObj, out env.newCollation)) + if (!TryProcessCollation(stateObj, out env._newCollation)) { return false; } // Give the parser the new collation values in case parameters don't specify one - _defaultCollation = env.newCollation; + _defaultCollation = env._newCollation; // UTF8 collation - if (env.newCollation.IsUTF8) + if (env._newCollation.IsUTF8) { _defaultEncoding = Encoding.UTF8; } else { - int newCodePage = GetCodePage(env.newCollation, stateObj); + int newCodePage = GetCodePage(env._newCollation, stateObj); if (newCodePage != _defaultCodePage) { _defaultCodePage = newCodePage; _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } } - _defaultLCID = env.newCollation.LCID; + _defaultLCID = env._newCollation.LCID; } if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!"); - if (env.oldLength == 5) + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 5 || env._oldLength == 0, "Improper length in old collation!"); + if (env._oldLength == 5) { - if (!TryProcessCollation(stateObj, out env.oldCollation)) + if (!TryProcessCollation(stateObj, out env._oldCollation)) { return false; } } - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_BEGINTRAN: @@ -2709,44 +2747,44 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = byteLength; - Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!"); + env._newLength = byteLength; + Debug.Assert(env._newLength == 0 || env._newLength == 8, "Improper length for new transaction id!"); - if (env.newLength > 0) + if (env._newLength > 0) { - if (!stateObj.TryReadInt64(out env.newLongValue)) + if (!stateObj.TryReadInt64(out env._newLongValue)) { return false; } - Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!"); + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 0 || env._oldLength == 8, "Improper length for old transaction id!"); - if (env.oldLength > 0) + if (env._oldLength > 0) { - if (!stateObj.TryReadInt64(out env.oldLongValue)) + if (!stateObj.TryReadInt64(out env._oldLongValue)) { return false; } - Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_LOGSHIPNODE: @@ -2759,12 +2797,12 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, break; case TdsEnums.ENV_PROMOTETRANSACTION: - if (!stateObj.TryReadInt32(out env.newLength)) + if (!stateObj.TryReadInt32(out env._newLength)) { // new value has 4 byte length return false; } - env.newBinValue = new byte[env.newLength]; - if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength)) + env._newBinValue = new byte[env._newLength]; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { // read new value with 4 byte length return false; } @@ -2773,11 +2811,11 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.oldLength = byteLength; - Debug.Assert(0 == env.oldLength, "old length should be zero"); + env._oldLength = byteLength; + Debug.Assert(0 == env._oldLength, "old length should be zero"); // env.length includes 1 byte for type token - env.length = 5 + env.newLength; + env._length = 5 + env._newLength; break; case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS: @@ -2801,7 +2839,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = newLength; + env._newLength = newLength; byte protocol; if (!stateObj.TryReadByte(out protocol)) { @@ -2822,7 +2860,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newRoutingInfo = new RoutingInfo(protocol, port, serverName); + env._newRoutingInfo = new RoutingInfo(protocol, port, serverName); ushort oldLength; if (!stateObj.TryReadUInt16(out oldLength)) { @@ -2832,14 +2870,14 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] + env._length = env._newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] break; default: - Debug.Fail("Unknown environment change token: " + env.type); + Debug.Fail("Unknown environment change token: " + env._type); break; } - processedLength += env.length; + processedLength += env._length; } sqlEnvChange = head; @@ -2854,10 +2892,10 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.newLength = byteLength; - env.newBinValue = ArrayPool.Shared.Rent(env.newLength); - env.newBinRented = true; - if (!stateObj.TryReadByteArray(env.newBinValue, env.newLength)) + env._newLength = byteLength; + env._newBinValue = ArrayPool.Shared.Rent(env._newLength); + env._newBinRented = true; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { return false; } @@ -2865,16 +2903,16 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.oldLength = byteLength; - env.oldBinValue = ArrayPool.Shared.Rent(env.oldLength); - env.oldBinRented = true; - if (!stateObj.TryReadByteArray(env.oldBinValue, env.oldLength)) + env._oldLength = byteLength; + env._oldBinValue = ArrayPool.Shared.Rent(env._oldLength); + env._oldBinRented = true; + if (!stateObj.TryReadByteArray(env._oldBinValue, env._oldLength)) { return false; } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; return true; } @@ -2900,13 +2938,13 @@ private bool TryReadTwoStringFields(SqlEnvChange env, TdsParserStateObject state return false; } - env.newLength = newLength; - env.newValue = newValue; - env.oldLength = oldLength; - env.oldValue = oldValue; + env._newLength = newLength; + env._newValue = newValue; + env._oldLength = oldLength; + env._oldValue = oldValue; // env.length includes 1 byte type token - env.length = 3 + env.newLength * 2 + env.oldLength * 2; + env._length = 3 + env._newLength * 2 + env._oldLength * 2; return true; } @@ -3148,7 +3186,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) bool ret = false; if (_connHandler._cleanSQLDNSCaching) { - ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCache); } if (_connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null @@ -3174,7 +3212,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) { // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.EnclaveComputationsNotSupported(); } @@ -3182,14 +3220,14 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) { throw SQL.AttestationURLNotSupported(); } - else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + else if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.AttestationProtocolNotSupported(); } } // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) || attestationProtocol == SqlConnectionAttestationProtocol.None) { if (string.IsNullOrWhiteSpace(EnclaveType)) { @@ -3200,7 +3238,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) // Check if the attestation protocol is specified and supports the enclave type. if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(attestationProtocol.ToString(), EnclaveType); } } } @@ -3215,10 +3253,8 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta { case TdsEnums.ENCLAVE_TYPE_VBS: if (attestationProtocol != SqlConnectionAttestationProtocol.AAS -#if ENCLAVE_SIMULATOR - && attestationProtocol != SqlConnectionAttestationProtocol.SIM -#endif - && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + && attestationProtocol != SqlConnectionAttestationProtocol.HGS + && attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3227,7 +3263,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta case TdsEnums.ENCLAVE_TYPE_SGX: #if ENCLAVE_SIMULATOR if (attestationProtocol != SqlConnectionAttestationProtocol.AAS - && attestationProtocol != SqlConnectionAttestationProtocol.SIM) + && attestationProtocol != SqlConnectionAttestationProtocol.None) #else if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) #endif @@ -3238,7 +3274,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta #if ENCLAVE_SIMULATOR case TdsEnums.ENCLAVE_TYPE_SIMULATOR: - if (attestationProtocol != SqlConnectionAttestationProtocol.SIM) + if (attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3252,26 +3288,6 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta return true; } - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; @@ -3618,17 +3634,24 @@ private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck s } _is2008 = true; break; - case TdsEnums.SQl2012_MAJOR << 24 | TdsEnums.SQL2012_MINOR: + case TdsEnums.SQL2012_MAJOR << 24 | TdsEnums.SQL2012_MINOR: if (increment != TdsEnums.SQL2012_INCREMENT) { throw SQL.InvalidTDSVersion(); } _is2012 = true; break; + case TdsEnums.TDS8_MAJOR << 24 | TdsEnums.TDS8_MINOR: + if (increment != TdsEnums.TDS8_INCREMENT) + { + throw SQL.InvalidTDSVersion(); + } + _is2022 = true; + break; default: throw SQL.InvalidTDSVersion(); } - + _is2012 |= _is2022; _is2008 |= _is2012; _is2005 |= _is2008; @@ -7928,7 +7951,7 @@ internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionD return len; } - internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData) + internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData, SqlConnectionEncryptOption encrypt) { _physicalStateObj.SetTimeoutSeconds(rec.timeout); @@ -8098,7 +8121,14 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures WriteInt(length, _physicalStateObj); if (recoverySessionData == null) { - WriteInt((TdsEnums.SQl2012_MAJOR << 24) | (TdsEnums.SQL2012_INCREMENT << 16) | TdsEnums.SQL2012_MINOR, _physicalStateObj); + if (encrypt == SqlConnectionEncryptOption.Strict) + { + WriteInt((TdsEnums.TDS8_MAJOR << 24) | (TdsEnums.TDS8_INCREMENT << 16) | TdsEnums.TDS8_MINOR, _physicalStateObj); + } + else + { + WriteInt((TdsEnums.SQL2012_MAJOR << 24) | (TdsEnums.SQL2012_INCREMENT << 16) | TdsEnums.SQL2012_MINOR, _physicalStateObj); + } } else { @@ -9440,7 +9470,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet int maxSupportedSize = Is2008OrNewer ? int.MaxValue : short.MaxValue; byte[] udtVal = null; - Format format = Format.Native; + SqlServer.Server.Format format = SqlServer.Server.Format.Native; if (string.IsNullOrEmpty(param.UdtTypeName)) { @@ -9831,7 +9861,6 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa value, typeCode, param.Offset, - 0 < param.Size ? param.Size : -1, peekAhead); } @@ -10776,7 +10805,8 @@ private void WriteMarsHeaderData(TdsParserStateObject stateObj, SqlInternalTrans } else { - WriteLong(SqlInternalTransaction.NullTransactionId, stateObj); + // If no transaction, send over retained transaction descriptor (empty if none retained) + WriteLong(_retainedTransactionId, stateObj); WriteInt(stateObj.IncrementAndObtainOpenResultCount(null), stateObj); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 6daecb969c..85f4588c8c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -94,70 +94,6 @@ internal RoutingInfo(byte protocol, ushort port, string servername) } } - internal sealed class SqlEnvChange - { - internal byte type; - internal byte oldLength; - internal int newLength; // 7206 TDS changes makes this length an int - internal int length; - internal string newValue; - internal string oldValue; - /// - /// contains binary data, before using this field check newBinRented to see if you can take the field array or whether you should allocate and copy - /// - internal byte[] newBinValue; - /// - /// contains binary data, before using this field check newBinRented to see if you can take the field array or whether you should allocate and copy - /// - internal byte[] oldBinValue; - internal long newLongValue; - internal long oldLongValue; - internal SqlCollation newCollation; - internal SqlCollation oldCollation; - internal RoutingInfo newRoutingInfo; - internal bool newBinRented; - internal bool oldBinRented; - - internal SqlEnvChange Next; - - internal void Clear() - { - type = 0; - oldLength = 0; - newLength = 0; - length = 0; - newValue = null; - oldValue = null; - if (newBinValue != null) - { - Array.Clear(newBinValue, 0, newBinValue.Length); - if (newBinRented) - { - ArrayPool.Shared.Return(newBinValue); - } - - newBinValue = null; - } - if (oldBinValue != null) - { - Array.Clear(oldBinValue, 0, oldBinValue.Length); - if (oldBinRented) - { - ArrayPool.Shared.Return(oldBinValue); - } - oldBinValue = null; - } - newBinRented = false; - oldBinRented = false; - newLongValue = 0; - oldLongValue = 0; - newCollation = null; - oldCollation = null; - newRoutingInfo = null; - Next = null; - } - } - internal sealed class SqlLogin { internal SqlAuthenticationMethod authentication = SqlAuthenticationMethod.NotSpecified; // Authentication type diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 980e0d556c..3db0f07fcf 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -145,7 +145,9 @@ internal SNIHandle( bool fSync, bool fParallel, SqlConnectionIPAddressPreference ipPreference, - SQLDNSInfo cachedDNSInfo) + SQLDNSInfo cachedDNSInfo, + bool tlsFirst, + string hostNameInCertificate) : base(IntPtr.Zero, true) { try @@ -159,8 +161,8 @@ internal SNIHandle( timeout = Timeout.Infinite; // -1 == native SNIOPEN_TIMEOUT_VALUE / INFINITE } - _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, - spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, ipPreference, cachedDNSInfo); + _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, spnBuffer, instanceName, flushCache, + fSync, timeout, fParallel, ipPreference, cachedDNSInfo, hostNameInCertificate); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index cbeb228ab9..21f4e9b61b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -394,7 +394,7 @@ internal object Owner } else { - _readerState = null; + _readerState = null; } _owner.SetTarget(value); } @@ -786,8 +786,22 @@ private void ResetCancelAndProcessAttention() } } - internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, - SqlConnectionIPAddressPreference iPAddressPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity = false); + internal abstract void CreatePhysicalSNIHandle( + string serverName, + bool ignoreSniOpenTimeout, + long timerExpire, + out byte[] instanceName, + ref byte[][] spnBuffer, + bool flushCache, + bool async, + bool fParallel, + SqlConnectionIPAddressPreference iPAddressPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + string serverSPN, + bool isIntegratedSecurity = false, + bool tlsFirst = false, + string hostNameInCertificate = ""); internal abstract void AssignPendingDNSInfo(string userProtocol, string DNSCacheKey, ref SQLDNSInfo pendingDNSInfo); @@ -799,7 +813,7 @@ internal abstract void CreatePhysicalSNIHandle(string serverName, bool ignoreSni protected abstract void FreeGcHandle(int remaining, bool release); - internal abstract uint EnableSsl(ref uint info); + internal abstract uint EnableSsl(ref uint info, bool tlsFirst); internal abstract uint WaitForSSLHandShakeToComplete(out int protocolVersion); @@ -1011,13 +1025,13 @@ internal Task ExecuteFlush() else { return AsyncHelper.CreateContinuationTaskWithState( - task: writePacketTask, + task: writePacketTask, state: this, - onSuccess: static (object state) => + onSuccess: static (object state) => { TdsParserStateObject stateObject = (TdsParserStateObject)state; stateObject.HasPendingData = true; - stateObject._messageStatus = 0; + stateObject._messageStatus = 0; } ); } @@ -1197,33 +1211,38 @@ private void SetSnapshottedState(SnapshottedStateFlags flag, bool value) } } + private bool GetSnapshottedState(SnapshottedStateFlags flag) + { + return (_snapshottedState & flag) == flag; + } + internal bool HasOpenResult { - get => _snapshottedState.HasFlag(SnapshottedStateFlags.OpenResult); + get => GetSnapshottedState(SnapshottedStateFlags.OpenResult); set => SetSnapshottedState(SnapshottedStateFlags.OpenResult, value); } internal bool HasPendingData { - get => _snapshottedState.HasFlag(SnapshottedStateFlags.PendingData); + get => GetSnapshottedState(SnapshottedStateFlags.PendingData); set => SetSnapshottedState(SnapshottedStateFlags.PendingData, value); } internal bool HasReceivedError { - get => _snapshottedState.HasFlag(SnapshottedStateFlags.ErrorTokenReceived); + get => GetSnapshottedState(SnapshottedStateFlags.ErrorTokenReceived); set => SetSnapshottedState(SnapshottedStateFlags.ErrorTokenReceived, value); } internal bool HasReceivedAttention { - get => _snapshottedState.HasFlag(SnapshottedStateFlags.AttentionReceived); + get => GetSnapshottedState(SnapshottedStateFlags.AttentionReceived); set => SetSnapshottedState(SnapshottedStateFlags.AttentionReceived, value); } internal bool HasReceivedColumnMetadata { - get => _snapshottedState.HasFlag(SnapshottedStateFlags.ColMetaDataReceived); + get => GetSnapshottedState(SnapshottedStateFlags.ColMetaDataReceived); set => SetSnapshottedState(SnapshottedStateFlags.ColMetaDataReceived, value); } @@ -2488,7 +2507,7 @@ internal void ReadSni(TaskCompletionSource completion) Timeout.Infinite, Timeout.Infinite ); - + // -1 == Infinite // 0 == Already timed out (NOTE: To simulate the same behavior as sync we will only timeout on 0 if we receive an IO Pending from SNI) @@ -2896,7 +2915,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error) Debug.Assert(CheckPacket(packet, source) && source != null, "AsyncResult null on callback"); if (_parser.MARSOn) - { + { // Only take reset lock on MARS and Async. CheckSetResetConnectionState(error, CallbackType.Read); } @@ -3616,7 +3635,7 @@ internal void SendAttention(bool mustTakeWriteLock = false) } } #if DEBUG - } + } #endif SetTimeoutSeconds(AttentionTimeoutSeconds); // Initialize new attention timeout of 5 seconds. @@ -4291,11 +4310,11 @@ internal void ResetSnapshotState() _stateObj._cleanupAltMetaDataSetArray = _snapshotCleanupAltMetaDataSetArray; // Make sure to go through the appropriate increment/decrement methods if changing the OpenResult flag - if (!_stateObj.HasOpenResult && _state.HasFlag(SnapshottedStateFlags.OpenResult)) + if (!_stateObj.HasOpenResult && ((_state & SnapshottedStateFlags.OpenResult) == SnapshottedStateFlags.OpenResult)) { _stateObj.IncrementAndObtainOpenResultCount(_stateObj._executedUnderTransaction); } - else if (_stateObj.HasOpenResult && !_state.HasFlag(SnapshottedStateFlags.OpenResult)) + else if (_stateObj.HasOpenResult && ((_state & SnapshottedStateFlags.OpenResult) != SnapshottedStateFlags.OpenResult)) { _stateObj.DecrementOpenResultCount(); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs index 4c3ad107b4..f4523f87b4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectManaged.cs @@ -2,18 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient.SNI { - internal class TdsParserStateObjectManaged : TdsParserStateObject + internal sealed class TdsParserStateObjectManaged : TdsParserStateObject { - private SNIMarsConnection _marsConnection; - private SNIHandle _sessionHandle; - private SspiClientContextStatus _sspiClientContextStatus; + private SNIMarsConnection? _marsConnection; + private SNIHandle? _sessionHandle; + private SspiClientContextStatus? _sspiClientContextStatus; public TdsParserStateObjectManaged(TdsParser parser) : base(parser) { } @@ -21,8 +26,6 @@ internal TdsParserStateObjectManaged(TdsParser parser, TdsParserStateObject phys base(parser, physicalConnection, async) { } - internal SNIHandle Handle => _sessionHandle; - internal override uint Status => _sessionHandle != null ? _sessionHandle.Status : TdsEnums.SNI_UNINITIALIZED; internal override SessionHandle SessionHandle => SessionHandle.FromManagedSession(_sessionHandle); @@ -36,14 +39,24 @@ protected override bool CheckPacket(PacketHandle packet, TaskCompletionSource _sessionHandle.Status != TdsEnums.SNI_SUCCESS; - - internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint error) + internal override bool IsFailedHandle() { - SNIHandle handle = Handle; - if (handle == null) + SNIHandle? sessionHandle = _sessionHandle; + if (sessionHandle is not null) { - throw ADP.ClosedConnectionError(); + return sessionHandle.Status != TdsEnums.SNI_SUCCESS; } + return true; + } + - error = handle.Receive(out SNIPacket packet, timeoutRemaining); - - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | Info | State Object Id {0}, Session Id {1}", _objectID, _sessionHandle?.ConnectionId); + internal override PacketHandle ReadSyncOverAsync(int timeoutRemaining, out uint error) + { + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); + + error = sessionHandle.Receive(out SNIPacket packet, timeoutRemaining); + + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | Info | State Object Id {0}, Session Id {1}", _objectID, sessionHandle.ConnectionId); #if DEBUG - SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | TRC | State Object Id {0}, Session Id {1}, Packet {2} received, Packet owner Id {3}, Packet dataLeft {4}", _objectID, _sessionHandle?.ConnectionId, packet?._id, packet?._owner.ConnectionId, packet?.DataLeft); + SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObjectManaged.ReadSyncOverAsync | TRC | State Object Id {0}, Session Id {1}, Packet {2} received, Packet owner Id {3}, Packet dataLeft {4}", _objectID, sessionHandle.ConnectionId, packet?._id, packet?._owner.ConnectionId, packet?.DataLeft); #endif return PacketHandle.FromManagedPacket(packet); } @@ -195,22 +235,31 @@ internal override void ReleasePacket(PacketHandle syncReadPacket) #if DEBUG SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObjectManaged.ReleasePacket | TRC | State Object Id {0}, Session Id {1}, Packet {2} will be released, Packet Owner Id {3}, Packet dataLeft {4}", _objectID, _sessionHandle?.ConnectionId, packet?._id, packet?._owner.ConnectionId, packet?.DataLeft); #endif - if (packet != null) + if (packet is not null) { - SNIHandle handle = Handle; - handle.ReturnPacket(packet); + SNIHandle? sessionHandle = _sessionHandle; + if (sessionHandle is not null) + { + sessionHandle.ReturnPacket(packet); + } + else + { + // clear the packet and drop it to GC because we no longer know how to return it to the correct owner + // this can only happen if a packet is in-flight when the _sessionHandle is cleared + packet.Release(); + } } } internal override uint CheckConnection() { - SNIHandle handle = Handle; - return handle == null ? TdsEnums.SNI_SUCCESS : handle.CheckConnection(); + SNIHandle? handle = GetSessionSNIHandleHandleOrThrow(); + return handle is null ? TdsEnums.SNI_SUCCESS : handle.CheckConnection(); } internal override PacketHandle ReadAsync(SessionHandle handle, out uint error) { - SNIPacket packet = null; + SNIPacket? packet = null; error = handle.ManagedHandle.ReceiveAsync(ref packet); SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.ReadAsync | Info | State Object Id {0}, Session Id {1}, Packet DataLeft {2}", _objectID, _sessionHandle?.ConnectionId, packet?.DataLeft); @@ -232,20 +281,21 @@ internal override PacketHandle CreateAndSetAttentionPacket() internal override uint WritePacket(PacketHandle packetHandle, bool sync) { - uint result; - SNIHandle handle = Handle; - SNIPacket packet = packetHandle.ManagedPacket; + uint result = TdsEnums.SNI_UNINITIALIZED; + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); + SNIPacket? packet = packetHandle.ManagedPacket; + if (sync) { - result = handle.Send(packet); - handle.ReturnPacket(packet); + result = sessionHandle.Send(packet); + sessionHandle.ReturnPacket(packet); } else { - result = handle.SendAsync(packet); + result = sessionHandle.SendAsync(packet); } - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.WritePacket | Info | Session Id {0}, SendAsync Result {1}", handle?.ConnectionId, result); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.WritePacket | Info | Session Id {0}, SendAsync Result {1}", sessionHandle.ConnectionId, result); return result; } @@ -264,12 +314,12 @@ internal override bool IsValidPacket(PacketHandle packet) internal override PacketHandle GetResetWritePacket(int dataSize) { - SNIHandle handle = Handle; - SNIPacket packet = handle.RentPacket(headerSize: handle.ReserveHeaderSize, dataSize: dataSize); + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); + SNIPacket packet = sessionHandle.RentPacket(headerSize: sessionHandle.ReserveHeaderSize, dataSize: dataSize); #if DEBUG Debug.Assert(packet.IsActive, "packet is not active, a serious pooling error may have occurred"); #endif - Debug.Assert(packet.ReservedHeaderSize == handle.ReserveHeaderSize, "failed to reserve header"); + Debug.Assert(packet.ReservedHeaderSize == sessionHandle.ReserveHeaderSize, "failed to reserve header"); return PacketHandle.FromManagedPacket(packet); } @@ -285,23 +335,24 @@ internal override void SetPacketData(PacketHandle packet, byte[] buffer, int byt internal override uint SniGetConnectionId(ref Guid clientConnectionId) { - clientConnectionId = Handle.ConnectionId; + clientConnectionId = GetSessionSNIHandleHandleOrThrow().ConnectionId; SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.GetConnectionId | Info | Session Id {0}", clientConnectionId); return TdsEnums.SNI_SUCCESS; } internal override uint DisableSsl() { - SNIHandle handle = Handle; - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.DisableSsl | Info | Session Id {0}", handle?.ConnectionId); - handle.DisableSsl(); + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.DisableSsl | Info | Session Id {0}", sessionHandle.ConnectionId); + sessionHandle.DisableSsl(); return TdsEnums.SNI_SUCCESS; } internal override uint EnableMars(ref uint info) { - _marsConnection = new SNIMarsConnection(Handle); - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableMars | Info | State Object Id {0}, Session Id {1}", _objectID, _sessionHandle?.ConnectionId); + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); + _marsConnection = new SNIMarsConnection(sessionHandle); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableMars | Info | State Object Id {0}, Session Id {1}", _objectID, sessionHandle.ConnectionId); if (_marsConnection.StartReceive() == TdsEnums.SNI_SUCCESS_IO_PENDING) { @@ -311,30 +362,30 @@ internal override uint EnableMars(ref uint info) return TdsEnums.SNI_ERROR; } - internal override uint EnableSsl(ref uint info) + internal override uint EnableSsl(ref uint info, bool tlsFirst) { - SNIHandle handle = Handle; + SNIHandle sessionHandle = GetSessionSNIHandleHandleOrThrow(); try { - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Info | Session Id {0}", handle?.ConnectionId); - return handle.EnableSsl(info); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Info | Session Id {0}", sessionHandle.ConnectionId); + return sessionHandle.EnableSsl(info); } catch (Exception e) { - SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Err | Session Id {0}, SNI Handshake failed with exception: {1}", handle?.ConnectionId, e?.Message); + SqlClientEventSource.Log.TryTraceEvent("TdsParserStateObjectManaged.EnableSsl | Err | Session Id {0}, SNI Handshake failed with exception: {1}", sessionHandle.ConnectionId, e.Message); return SNICommon.ReportSNIError(SNIProviders.SSL_PROV, SNICommon.HandshakeFailureError, e); } } internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) { - Handle.SetBufferSize((int)unsignedPacketSize); + GetSessionSNIHandleHandleOrThrow().SetBufferSize((int)unsignedPacketSize); return TdsEnums.SNI_SUCCESS; } internal override uint GenerateSspiClientContext(byte[] receivedBuff, uint receivedLength, ref byte[] sendBuff, ref uint sendLength, byte[][] _sniSpnBuffer) { - if (_sspiClientContextStatus == null) + if (_sspiClientContextStatus is null) { _sspiClientContextStatus = new SspiClientContextStatus(); } @@ -347,8 +398,22 @@ internal override uint GenerateSspiClientContext(byte[] receivedBuff, uint recei internal override uint WaitForSSLHandShakeToComplete(out int protocolVersion) { - protocolVersion = Handle.ProtocolVersion; + protocolVersion = GetSessionSNIHandleHandleOrThrow().ProtocolVersion; return 0; } + + private SNIHandle GetSessionSNIHandleHandleOrThrow() + { + SNIHandle? sessionHandle = _sessionHandle; + if (sessionHandle is null) + { + ThrowClosedConnection(); + } + return sessionHandle; + } + + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] // this forces the exception throwing code not to be inlined for performance + private void ThrowClosedConnection() => throw ADP.ClosedConnectionError(); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs index 89df41b417..99bbc9bf53 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObjectNative.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.Data.Common; using System.Net; +using System.Text; namespace Microsoft.Data.SqlClient { @@ -64,7 +65,7 @@ protected override void CreateSessionHandle(TdsParserStateObject physicalConnect SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); SQLDNSInfo cachedDNSInfo; - bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCache, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, nativeSNIObject.Handle, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); } @@ -137,15 +138,40 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, ref byte[][] spnBuffer, bool flushCache, bool async, bool fParallel, - SqlConnectionIPAddressPreference ipPreference, string cachedFQDN, ref SQLDNSInfo pendingDNSInfo, bool isIntegratedSecurity) + internal override void CreatePhysicalSNIHandle( + string serverName, + bool ignoreSniOpenTimeout, + long timerExpire, + out byte[] instanceName, + ref byte[][] spnBuffer, + bool flushCache, + bool async, + bool fParallel, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + ref SQLDNSInfo pendingDNSInfo, + string serverSPN, + bool isIntegratedSecurity, + bool tlsFirst, + string hostNameInCertificate) { // We assume that the loadSSPILibrary has been called already. now allocate proper length of buffer spnBuffer = new byte[1][]; if (isIntegratedSecurity) { // now allocate proper length of buffer - spnBuffer[0] = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength]; + if (!string.IsNullOrEmpty(serverSPN)) + { + // Native SNI requires the Unicode encoding and any other encoding like UTF8 breaks the code. + byte[] srvSPN = Encoding.Unicode.GetBytes(serverSPN); + Trace.Assert(srvSPN.Length <= SNINativeMethodWrapper.SniMaxComposedSpnLength, "Length of the provided SPN exceeded the buffer size."); + spnBuffer[0] = srvSPN; + SqlClientEventSource.Log.TryTraceEvent("<{0}.{1}|SEC> Server SPN `{2}` from the connection string is used.",nameof(TdsParserStateObjectNative), nameof(CreatePhysicalSNIHandle), serverSPN); + } + else + { + spnBuffer[0] = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength]; + } } SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); @@ -172,7 +198,8 @@ internal override void CreatePhysicalSNIHandle(string serverName, bool ignoreSni SQLDNSInfo cachedDNSInfo; bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer[0], ignoreSniOpenTimeout, checked((int)timeout), out instanceName, flushCache, !async, fParallel, ipPreference, cachedDNSInfo); + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer[0], ignoreSniOpenTimeout, checked((int)timeout), out instanceName, + flushCache, !async, fParallel, ipPreference, cachedDNSInfo, tlsFirst, hostNameInCertificate); } protected override uint SNIPacketGetData(PacketHandle packet, byte[] _inBuff, ref uint dataSize) @@ -376,10 +403,14 @@ internal override uint DisableSsl() internal override uint EnableMars(ref uint info) => SNINativeMethodWrapper.SNIAddProvider(Handle, SNINativeMethodWrapper.ProviderEnum.SMUX_PROV, ref info); - internal override uint EnableSsl(ref uint info) + internal override uint EnableSsl(ref uint info, bool tlsFirst) { + SNINativeMethodWrapper.AuthProviderInfo authInfo = new SNINativeMethodWrapper.AuthProviderInfo(); + authInfo.flags = info; + authInfo.tlsFirst = tlsFirst; + // Add SSL (Encryption) SNI provider. - return SNINativeMethodWrapper.SNIAddProvider(Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref info); + return SNINativeMethodWrapper.SNIAddProvider(Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, ref authInfo); } internal override uint SetConnectionBufferSize(ref uint unsignedPacketSize) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs index e17a65be95..6f67764f9a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs @@ -180,5 +180,6 @@ internal static int GetRemainingTimeout(int timeout, long start) return checked((int)remaining); } } + internal static long GetTimeoutSeconds(int timeout) => GetTimeout((long)timeout * 1000L); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs new file mode 100644 index 0000000000..43ed8bbee4 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Data.SqlTypes; +using System.Runtime.InteropServices; + +namespace Microsoft.Data.SqlTypes +{ + /// + /// This type provides workarounds for the separation between System.Data.Common + /// and Microsoft.Data.SqlClient. The latter wants to access internal members of the former, and + /// this class provides ways to do that. We must review and update this implementation any time the + /// implementation of the corresponding types in System.Data.Common change. + /// + internal static partial class SqlTypeWorkarounds + { + #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation + /// + /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists + /// only to distinguish this constructor from the constructor that takes a long. + /// Used only internally. + /// + internal static SqlMoney SqlMoneyCtor(long value, int ignored) + { + var c = default(SqlMoneyCaster); + + // Same behavior as the internal SqlMoney.ctor(long, bool) overload + c.Fake._fNotNull = true; + c.Fake._value = value; + + return c.Real; + } + + internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money) + { + var c = default(SqlMoneyCaster); + c.Real = money; + + // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation + if (money.IsNull) + { + throw new SqlNullValueException(); + } + return c.Fake._value; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields + { + internal bool _fNotNull; + internal long _value; + } + + [StructLayout(LayoutKind.Explicit)] + private struct SqlMoneyCaster + { + [FieldOffset(0)] + internal SqlMoney Real; + [FieldOffset(0)] + internal SqlMoneyLookalike Fake; + } + #endregion + + #region Work around inability to access SqlDecimal._data1/2/3/4 + internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) + { + // Extract the four data elements from SqlDecimal. + var c = default(SqlDecimalCaster); + c.Real = d; + data1 = c.Fake._data1; + data2 = c.Fake._data2; + data3 = c.Fake._data3; + data4 = c.Fake._data4; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields + { + internal byte _bStatus; + internal byte _bLen; + internal byte _bPrec; + internal byte _bScale; + internal uint _data1; + internal uint _data2; + internal uint _data3; + internal uint _data4; + } + + [StructLayout(LayoutKind.Explicit)] + private struct SqlDecimalCaster + { + [FieldOffset(0)] + internal SqlDecimal Real; + [FieldOffset(0)] + internal SqlDecimalLookalike Fake; + } + #endregion + + #region Work around inability to access SqlBinary.ctor(byte[], bool) + internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) + { + // Construct a SqlBinary without allocating/copying the byte[]. This provides + // the same behavior as SqlBinary.ctor(byte[], bool). + var c = default(SqlBinaryCaster); + c.Fake._value = value; + return c.Real; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SqlBinaryLookalike + { + internal byte[] _value; + } + + [StructLayout(LayoutKind.Explicit)] + private struct SqlBinaryCaster + { + [FieldOffset(0)] + internal SqlBinary Real; + [FieldOffset(0)] + internal SqlBinaryLookalike Fake; + } + #endregion + + #region Work around inability to access SqlGuid.ctor(byte[], bool) + internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) + { + // Construct a SqlGuid without allocating/copying the byte[]. This provides + // the same behavior as SqlGuid.ctor(byte[], bool). + var c = default(SqlGuidCaster); + c.Fake._value = value; + return c.Real; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SqlGuidLookalike + { + internal byte[] _value; + } + + [StructLayout(LayoutKind.Explicit)] + private struct SqlGuidCaster + { + [FieldOffset(0)] + internal SqlGuid Real; + [FieldOffset(0)] + internal SqlGuidLookalike Fake; + } + #endregion + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml index af9532a1ae..73ddc2f575 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml @@ -197,7 +197,7 @@ 4 4 SQLCommand - SELECT DB_NAME() AS TYPE_CATALOG, sc.name AS TYPE_SCHEMA, tt.name AS TYPE_NAME, c.name AS MEMBER_NAME, ColumnProperty(c.object_id, c.name, 'ordinal') AS ORDINAL_POSITION, convert(nvarchar(4000), object_definition(c.default_object_id)) AS MEMBER_DEFAULT, convert(varchar(3), CASE c.is_nullable WHEN 1 THEN 'YES' ELSE 'NO' END) AS IS_NULLABLE, type_name(c.system_type_id) AS DATA_TYPE, ColumnProperty(c.object_id, c.name, 'charmaxlen') AS CHARACTER_MAXIMUM_LENGTH, ColumnProperty(c.object_id, c.name, 'octetmaxlen') AS CHARACTER_OCTET_LENGTH, convert(tinyint, CASE /* int/decimal/numeric/real/float/money */ WHEN c.system_type_id IN (48, 52, 56, 59, 60, 62, 106, 108, 122, 127) THEN c.precision END) AS NUMERIC_PRECISION, convert(smallint, CASE /* int/money/decimal/numeric */ WHEN c.system_type_id IN (48, 52, 56, 60, 106, 108, 122, 127) THEN 10 WHEN c.system_type_id IN (59, 62) THEN 2 END) AS NUMERIC_PRECISION_RADIX, /* real/float */ convert(int, CASE /* datetime/smalldatetime */ WHEN c.system_type_id IN (58, 61) THEN NULL ELSE odbcscale(c.system_type_id, c.scale) END) AS NUMERIC_SCALE, convert(smallint, CASE /* datetime/smalldatetime */ WHEN c.system_type_id IN (58, 61) THEN 3 END) AS DATETIME_PRECISION, convert(sysname, null) AS CHARACTER_SET_CATALOG, convert(sysname, null) AS CHARACTER_SET_SCHEMA, convert(sysname, CASE WHEN c.system_type_id IN (35, 167, 175) /*char/varchar/text*/ THEN CollationProperty(c.collation_name, 'sqlcharsetname') WHEN c.system_type_id IN (99, 231, 239) /*nchar/nvarchar/ntext*/ THEN N'UNICODE' END) AS CHARACTER_SET_NAME, convert(sysname, null) AS COLLATION_CATALOG FROM sys.schemas sc join sys.objects o on sc.schema_id = o.schema_id JOIN sys.table_types tt on o.object_id = tt.type_table_object_id JOIN sys.columns c ON c.object_id = o.object_id LEFT JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE o.type IN ('TT') AND (DB_NAME() = @Catalog or (@Catalog is null)) and (sc.name = @Owner or (@Owner is null)) and (tt.name = @Type or (@Type is null)) and (c.name = @Member or (@Member is null)) order by sc.name, tt.name, c.name + SELECT DB_NAME() AS TYPE_CATALOG, sc.name AS TYPE_SCHEMA, tt.name AS TYPE_NAME, c.name AS MEMBER_NAME, ColumnProperty(c.object_id, c.name, 'ordinal') AS ORDINAL_POSITION, convert(nvarchar(4000), object_definition(c.default_object_id)) AS MEMBER_DEFAULT, convert(varchar(3), CASE c.is_nullable WHEN 1 THEN 'YES' ELSE 'NO' END) AS IS_NULLABLE, type_name(c.system_type_id) AS DATA_TYPE, ColumnProperty(c.object_id, c.name, 'charmaxlen') AS CHARACTER_MAXIMUM_LENGTH, ColumnProperty(c.object_id, c.name, 'octetmaxlen') AS CHARACTER_OCTET_LENGTH, convert(tinyint, CASE WHEN c.system_type_id IN /* int/decimal/numeric/real/float/money */ (48, 52, 56, 59, 60, 62, 106, 108, 122, 127) THEN c.precision END) AS NUMERIC_PRECISION, convert(smallint, CASE WHEN c.system_type_id IN /* int/money/decimal/numeric */ (48, 52, 56, 60, 106, 108, 122, 127) THEN 10 WHEN c.system_type_id IN /* real/float */ (59, 62) THEN 2 END) AS NUMERIC_PRECISION_RADIX, convert(int, CASE WHEN c.system_type_id IN /* datetime/smalldatetime */ (58, 61) THEN NULL ELSE odbcscale(c.system_type_id, c.scale) END) AS NUMERIC_SCALE, convert(smallint, CASE WHEN c.system_type_id IN /* datetime/smalldatetime */ (58, 61) THEN 3 END) AS DATETIME_PRECISION, convert(sysname, null) AS CHARACTER_SET_CATALOG, convert(sysname, null) AS CHARACTER_SET_SCHEMA, convert(sysname, CASE WHEN c.system_type_id IN /* char/varchar/text */ (35, 167, 175) THEN CollationProperty(c.collation_name, 'sqlcharsetname') WHEN c.system_type_id IN /* nchar/nvarchar/ntext */ (99, 231, 239) THEN N'UNICODE' END) AS CHARACTER_SET_NAME, convert(sysname, null) AS COLLATION_CATALOG FROM sys.table_types tt join sys.objects o on o.object_id = tt.type_table_object_id JOIN sys.schemas sc on sc.schema_id = tt.schema_id JOIN sys.columns c ON c.object_id = o.object_id LEFT JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE o.type IN ('TT') AND (DB_NAME() = @Catalog or (@Catalog is null)) and (sc.name = @Owner or (@Owner is null)) and (tt.name = @Type or (@Type is null)) and (c.name = @Member or (@Member is null)) order by sc.name, tt.name, c.name 09.00.000.0 diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index f33751fb08..ccbc9db180 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -60,6 +60,15 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2}. + /// + internal static string AAD_Token_Retrieving_Timeout { + get { + return ResourceManager.GetString("AAD_Token_Retrieving_Timeout", resourceCulture); + } + } + /// /// Looks up a localized string similar to Specified parameter name '{0}' is not valid.. /// @@ -941,7 +950,7 @@ internal static string Data_InvalidOffsetLength { return ResourceManager.GetString("Data_InvalidOffsetLength", resourceCulture); } } - + /// /// Looks up a localized string similar to Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services.. /// @@ -1905,6 +1914,15 @@ internal static string SNI_ERROR_9 { } } + /// + /// Looks up a localized string similar to Incorrect physicalConnection type. + /// + internal static string SNI_IncorrectPhysicalConnectionType { + get { + return ResourceManager.GetString("SNI_IncorrectPhysicalConnectionType", resourceCulture); + } + } + /// /// Looks up a localized string similar to HTTP Provider. /// @@ -3228,6 +3246,15 @@ internal static string SQL_StreamWriteNotSupported { } } + /// + /// Looks up a localized string similar to Encrypt=Strict is not supported when targeting .NET Standard 2.0. Use .NET Standard 2.1, .NET Framework, or .NET.. + /// + internal static string SQL_TDS8_NotSupported_Netstandard2_0 { + get { + return ResourceManager.GetString("SQL_TDS8_NotSupported_Netstandard2.0", resourceCulture); + } + } + /// /// Looks up a localized string similar to Processing of results from SQL Server failed because of an invalid multipart name. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index bfe5559389..f268baa2de 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1932,4 +1932,13 @@ Parameter '{0}' cannot have Direction Output or InputOutput when EnableOptimizedParameterBinding is enabled on the parent command. - \ No newline at end of file + + Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2} + + + Incorrect physicalConnection type. + + + Encrypt=Strict is not supported when targeting .NET Standard 2.0. Use .NET Standard 2.1, .NET Framework, or .NET. + + diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs index 4b61d284ff..f4bfdbe67d 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs @@ -76,6 +76,7 @@ internal class ResourceNames { internal const string DataCategory_Data = @"Data"; internal const string DataCategory_Update = @"Update"; + internal const string DataCategory_Xml = @"XML"; internal const string DbCommand_CommandTimeout = @"Time to wait for command to execute."; internal const string DbConnection_State = @"The ConnectionState indicating whether the connection is open or closed."; internal const string DataCategory_Fill = @"Fill"; @@ -103,8 +104,9 @@ internal class ResourceNames internal const string DbConnectionString_DataSource = @"Indicates the name of the data source to connect to."; internal const string DbConnectionString_Encrypt = @"When true, SQL Server uses SSL encryption for all data sent between the client and server if the server has a certificate installed."; internal const string DbConnectionString_Enlist = @"Sessions in a Component Services (or MTS, if you are using Microsoft Windows NT) environment should automatically be enlisted in a global transaction where required."; - internal const string DbConnectionString_InitialCatalog = @"The name of the initial catalog or database in the data source."; internal const string DbConnectionString_FailoverPartner = @"The name or network address of the instance of SQL Server that acts as a failover partner."; + internal const string DbConnectionString_FailoverPartnerSPN = @"The service principal name (SPN) of the failover partner."; + internal const string DbConnectionString_InitialCatalog = @"The name of the initial catalog or database in the data source."; internal const string DbConnectionString_IntegratedSecurity = @"Whether the connection is to be a secure connection or not."; internal const string DbConnectionString_LoadBalanceTimeout = @"The minimum amount of time (in seconds) for this connection to live in the pool before being destroyed."; internal const string DbConnectionString_MaxPoolSize = @"The maximum number of connections allowed in the pool."; @@ -116,6 +118,7 @@ internal class ResourceNames internal const string DbConnectionString_PersistSecurityInfo = @"When false, security-sensitive information, such as the password, is not returned as part of the connection if the connection is open or has ever been in an open state."; internal const string DbConnectionString_Pooling = @"When true, the connection object is drawn from the appropriate pool, or if necessary, is created and added to the appropriate pool."; internal const string DbConnectionString_Replication = @"Used by SQL Server in Replication."; + internal const string DbConnectionString_ServerSPN = @"The service principal name (SPN) of the server."; internal const string DbConnectionString_TransactionBinding = @"Indicates binding behavior of connection to a System.Transactions Transaction when enlisted."; internal const string DbConnectionString_TrustServerCertificate = @"When true (and encrypt=true), SQL Server uses SSL encryption for all data sent between the client and server without validating the server certificate."; internal const string DbConnectionString_TypeSystemVersion = @"Indicates which server type system the provider will expose through the DataReader."; diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index eca9091a58..5d78fb574e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -34,6 +34,15 @@ public SqlNotificationRequest(string userData, string options, int timeout) { } /// public string UserData { get { throw null; } set { } } } + + /// + public sealed class SqlDataSourceEnumerator : System.Data.Common.DbDataSourceEnumerator + { + /// + public static SqlDataSourceEnumerator Instance {get;} + /// + public override System.Data.DataTable GetDataSources(){ throw null; } + } } namespace Microsoft.Data.SqlClient @@ -424,6 +433,8 @@ public static partial class SqlClientMetaDataCollectionNames public static readonly string AllColumns; /// public static readonly string ColumnSetColumns; + /// + public static readonly string StructuredTypeMembers; } /// public sealed partial class SqlClientPermission : System.Data.Common.DBDataPermission @@ -889,10 +900,8 @@ public enum SqlConnectionAttestationProtocol /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, /// HGS = 3 @@ -911,6 +920,40 @@ public enum SqlConnectionIPAddressPreference UsePlatformDefault = 2 } + /// + public sealed class SqlConnectionEncryptOption + { + /// + public static SqlConnectionEncryptOption Parse(string value) => throw null; + + /// + public static bool TryParse(string value, out SqlConnectionEncryptOption result) => throw null; + + /// + public static SqlConnectionEncryptOption Optional => throw null; + + /// + public static SqlConnectionEncryptOption Mandatory => throw null; + + /// + public static SqlConnectionEncryptOption Strict => throw null; + + /// + public static implicit operator SqlConnectionEncryptOption(bool value) => throw null; + + /// + public static implicit operator bool(SqlConnectionEncryptOption value) => throw null; + + /// + public override string ToString() { throw null; } + + /// + public override bool Equals(object obj) { throw null; } + + /// + public override int GetHashCode() { throw null; } + } + /// public enum SqlConnectionOverrides { @@ -999,7 +1042,7 @@ public SqlConnectionStringBuilder(string connectionString) { } /// [System.ComponentModel.DisplayNameAttribute("Encrypt")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] - public bool Encrypt { get { throw null; } set { } } + public SqlConnectionEncryptOption Encrypt { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Enlist")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -1008,6 +1051,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Failover Partner")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public string FailoverPartner { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Failover Partner SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string FailoverPartnerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Initial Catalog")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -1071,6 +1118,10 @@ public SqlConnectionStringBuilder(string connectionString) { } [System.ComponentModel.DisplayNameAttribute("Replication")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] public bool Replication { get { throw null; } set { } } + /// + [System.ComponentModel.DisplayNameAttribute("Server SPN")] + [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] + public string ServerSPN { get { throw null; } set { } } /// [System.ComponentModel.DisplayNameAttribute("Transaction Binding")] [System.ComponentModel.RefreshPropertiesAttribute(System.ComponentModel.RefreshProperties.All)] @@ -1888,41 +1939,6 @@ public sealed class SqlConfigurableRetryFactory } namespace Microsoft.Data.SqlClient.Server { - /// - public enum DataAccessKind - { - /// - None = 0, - /// - Read = 1 - } - /// - public enum Format - { - /// - Unknown = 0, - /// - Native = 1, - /// - UserDefined = 2 - } - /// - public interface IBinarySerialize - { - /// - void Read(System.IO.BinaryReader r); - /// - void Write(System.IO.BinaryWriter w); - } - - /// - [System.Serializable] - public sealed partial class InvalidUdtException : System.SystemException - { - internal InvalidUdtException() { } - private InvalidUdtException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } - - } /// public partial class SqlDataRecord : System.Data.IDataRecord { @@ -2093,44 +2109,6 @@ public virtual void SetValue(int ordinal, object value) { } /// public virtual int SetValues(params object[] values) { throw null; } } - /// - [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue | System.AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public partial class SqlFacetAttribute : System.Attribute - { - /// - public SqlFacetAttribute() { } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsNullable { get { throw null; } set { } } - /// - public int MaxSize { get { throw null; } set { } } - /// - public int Precision { get { throw null; } set { } } - /// - public int Scale { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public partial class SqlFunctionAttribute : System.Attribute - { - /// - public SqlFunctionAttribute() { } - /// - public bool IsDeterministic { get { throw null; } set { } } - /// - public DataAccessKind DataAccess { get { throw null; } set { } } - /// - public SystemDataAccessKind SystemDataAccess { get { throw null; } set { } } - /// - public bool IsPrecise { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - /// - public string TableDefinition { get { throw null; } set { } } - /// - public string FillRowMethodName { get { throw null; } set { } } - } /// public sealed partial class SqlMetaData { @@ -2269,69 +2247,6 @@ public SqlMetaData(string name, System.Data.SqlDbType dbType, System.Type userDe /// public static Microsoft.Data.SqlClient.Server.SqlMetaData InferFromValue(object value, string name) { throw null; } } - /// - [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false, Inherited = false), System.SerializableAttribute] - public sealed partial class SqlMethodAttribute : SqlFunctionAttribute - { - /// - public SqlMethodAttribute() { } - /// - public bool OnNullCall { get { throw null; } set { } } - /// - public bool IsMutator { get { throw null; } set { } } - /// - public bool InvokeIfReceiverIsNull { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] - public sealed partial class SqlUserDefinedAggregateAttribute : System.Attribute - { - /// - public const int MaxByteSizeValue = 8000; - /// - public SqlUserDefinedAggregateAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsInvariantToDuplicates { get { throw null; } set { } } - /// - public bool IsInvariantToNulls { get { throw null; } set { } } - /// - public bool IsInvariantToOrder { get { throw null; } set { } } - /// - public bool IsNullIfEmpty { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string Name { get { throw null; } set { } } - } - /// - [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] - public sealed partial class SqlUserDefinedTypeAttribute : System.Attribute - { - /// - public SqlUserDefinedTypeAttribute(Format format) { } - /// - public int MaxByteSize { get { throw null; } set { } } - /// - public bool IsFixedLength { get { throw null; } set { } } - /// - public bool IsByteOrdered { get { throw null; } set { } } - /// - public Format Format { get { throw null; } } - /// - public string ValidationMethodName { get { throw null; } set { } } - /// - public string Name { get { throw null; } set { } } - } - /// - public enum SystemDataAccessKind - { - /// - None = 0, - /// - Read = 1 - } } namespace Microsoft.Data.SqlClient.DataClassification { diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 44efea9b8f..7a80ce32da 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -1,7 +1,7 @@  false - net461 + net462 $(ObjFolder)$(Configuration)\$(AssemblyName)\ref\ $(BinFolder)$(Configuration)\$(AssemblyName)\ref\ $(OutputPath)\Microsoft.Data.SqlClient.xml diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 96085bcda3..12323365bf 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -4,7 +4,7 @@ {407890AC-9876-4FEF-A6F1-F36A876BAADE} SqlClient - v4.6.1 + v4.6.2 true Strings SqlClient.Resources.$(ResxFileName) @@ -89,21 +89,21 @@ - - Microsoft\Data\SqlClient\LocalAppContextSwitches.cs - - - Microsoft\Data\SqlClient\SqlClientEventSource.cs - - - Microsoft\Data\SqlClient\SqlClientLogger.cs - Microsoft\Data\Common\ActivityCorrelator.cs + + Microsoft\Data\Common\AdapterUtil.cs + + + Microsoft\Data\Common\AdapterUtil.Windows.cs + Microsoft\Data\Common\DbConnectionStringCommon.cs + + Microsoft\Data\Common\DbConnectionOptions.Common.cs + Microsoft\Data\Common\DbConnectionPoolKey.cs @@ -113,29 +113,29 @@ Microsoft\Data\Common\NameValuePair.cs - - Microsoft\Data\Common\DbConnectionOptions.Common.cs + + Microsoft\Data\DataException.cs - - Microsoft\Data\Sql\SqlNotificationRequest.cs + + Microsoft\Data\OperationAbortedException.cs - - Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolGroup.cs Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs - - Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolProviderInfo.cs Microsoft\Data\ProviderBase\DbMetaDataFactory.cs @@ -146,6 +146,21 @@ Microsoft\Data\ProviderBase\TimeoutTimer.cs + + Microsoft\Data\Sql\SqlDataSourceEnumerator.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumerator.Windows.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs + + + Microsoft\Data\Sql\SqlDataSourceEnumeratorUtil.cs + + + Microsoft\Data\Sql\SqlNotificationRequest.cs + Microsoft\Data\SqlClient\ActiveDirectoryAuthenticationTimeoutRetryHelper.cs @@ -161,12 +176,14 @@ Microsoft\Data\SqlClient\AlwaysEncryptedEnclaveProviderUtils.cs - Microsoft\Data\SqlClient\AzureAttestationBasedEnclaveProvider.cs - - Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs + + Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs + + + Microsoft\Data\SqlClient\DataClassification\SensitivityClassification.cs Microsoft\Data\SqlClient\EnclaveDelegate.cs @@ -177,12 +194,69 @@ Microsoft\Data\SqlClient\EnclavePackage.cs + + Microsoft\Data\SqlClient\EnclaveProviderBase.cs + + + Microsoft\Data\SqlClient\EnclaveSessionCache.cs + + + Microsoft\Data\SqlClient\LocalAppContextSwitches.cs + + + Microsoft\Data\SqlClient\NoneAttestationEnclaveProvider.cs + + + Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + + Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs + + + Microsoft\Data\SqlClient\PoolBlockingPeriod.cs + + + Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalBaseEnumerator.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogic.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBase.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBaseProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryLogicProvider.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryFactory.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + + Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + + Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalEnumerators.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs + + + Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + Microsoft\Data\SqlClient\Server\ExtendedClrTypeCode.cs - - Microsoft\Data\SqlClient\Server\IBinarySerialize.cs - Microsoft\Data\SqlClient\Server\ITypedGetters.cs @@ -198,47 +272,59 @@ Microsoft\Data\SqlClient\Server\MemoryRecordBuffer.cs + + Microsoft\Data\SqlClient\Server\MetadataUtilsSmi.cs + + + Microsoft\Data\SqlClient\Server\SmiEventSink.cs + + + Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs + + + Microsoft\Data\SqlClient\Server\SmiEventSink_Default.netfx.cs + Microsoft\Data\SqlClient\Server\SmiGettersStream.cs + + Microsoft\Data\SqlClient\Server\SmiMetaData.cs + + + Microsoft\Data\SqlClient\Server\SmiMetaDataProperty.cs + + + Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs + Microsoft\Data\SqlClient\Server\SmiSettersStream.cs - - Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs + + Microsoft\Data\SqlClient\Server\SmiTypedGetterSetter.cs Microsoft\Data\SqlClient\Server\SmiXetterAccess.Common.cs - - Microsoft\Data\SqlClient\Server\SqlFacetAttribute.cs + + Microsoft\Data\SqlClient\Server\SmiXetterTypeCode.cs + + + Microsoft\Data\SqlClient\Server\SqlDataRecord.cs - - Microsoft\Data\SqlClient\Server\SqlFunctionAttribute.cs + + Microsoft\Data\SqlClient\Server\SqlDataRecord.netfx.cs Microsoft\Data\SqlClient\Server\SqlMetaData.cs - - Microsoft\Data\SqlClient\Server\SqlMethodAttribute.cs - - - Microsoft\Data\SqlClient\Server\SqlUserDefinedTypeAttribute.cs - - - Microsoft\Data\SqlClient\Server\SqlUserDefinedAggregateAttribute.cs + + Microsoft\Data\SqlClient\Server\SqlNormalizer.cs Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs - - Microsoft\Data\SqlClient\Server\SqlDataRecord.cs - - - Microsoft\Data\SqlClient\Server\SqlDataRecord.netfx.cs - - - Microsoft\Data\SqlClient\Server\SmiMetaData.cs + + Microsoft\Data\SqlClient\SqlTransaction.Common.cs Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs @@ -246,42 +332,15 @@ Microsoft\Data\SqlClient\Server\ValueUtilsSmi.netfx.cs - - Microsoft\Data\SqlClient\Server\SmiMetaDataProperty.cs - - - Microsoft\Data\SqlClient\ColumnEncryptionKeyInfo.cs - - - Microsoft\Data\SqlClient\Server\SmiEventSink.cs - - - Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs - - - Microsoft\Data\SqlClient\Server\SmiEventSink_Default.netfx.cs - - - Microsoft\Data\SqlClient\OnChangedEventHandler.cs + + Microsoft\Data\SqlClient\Server\SqlSer.cs - - Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs - - - Microsoft\Data\SqlClient\PoolBlockingPeriod.cs - - - Microsoft\Data\SqlClient\RowsCopiedEventArgs.cs - - - Microsoft\Data\SqlClient\RowsCopiedEventHandler.cs + + Microsoft\Data\SqlClient\SignatureVerificationCache.cs Microsoft\Data\SqlClient\SortOrder.cs - - Microsoft\Data\SqlClient\SqlAuthenticationToken.cs - Microsoft\Data\SqlClient\SqlAeadAes256CbcHmac256Algorithm.cs @@ -297,21 +356,27 @@ Microsoft\Data\SqlClient\SqlAuthenticationProvider.cs + + Microsoft\Data\SqlClient\SqlBuffer.cs + + + Microsoft\Data\SqlClient\SqlAuthenticationToken.cs + Microsoft\Data\SqlClient\SqlBulkCopyColumnMapping.cs Microsoft\Data\SqlClient\SqlBulkCopyColumnMappingCollection.cs - - Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs - Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHint.cs Microsoft\Data\SqlClient\SqlBulkCopyColumnOrderHintCollection.cs + + Microsoft\Data\SqlClient\SqlBulkCopyOptions.cs + Microsoft\Data\SqlClient\SqlCachedBuffer.cs @@ -327,6 +392,12 @@ Microsoft\Data\SqlClient\SqlClientEncryptionType.cs + + Microsoft\Data\SqlClient\SqlClientEventSource.cs + + + Microsoft\Data\SqlClient\SqlClientLogger.cs + Microsoft\Data\SqlClient\SqlClientMetaDataCollectionNames.cs @@ -341,11 +412,13 @@ Microsoft\Data\SqlClient\SqlCommandBuilder.cs - Component Microsoft\Data\SqlClient\SqlCommandSet.cs + + Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs + Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs @@ -358,6 +431,15 @@ Microsoft\Data\SqlClient\SqlConnectionString.cs + + Microsoft\Data\SqlClient\SqlConnectionStringBuilder.cs + + + Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs + + + Microsoft\Data\SqlClient\SqlCredential.cs + Microsoft\Data\SqlClient\SqlDataAdapter.cs @@ -367,6 +449,9 @@ Microsoft\Data\SqlClient\SqlDependencyListener.cs + + Microsoft\Data\SqlClient\SqlDependencyUtils.cs + Microsoft\Data\SqlClient\SqlEnclaveAttestationParameters.Crypto.cs @@ -376,18 +461,30 @@ Microsoft\Data\SqlClient\SqlEnums.cs + + Microsoft\Data\SqlClient\SqlEnvChange.cs + + + Microsoft\Data\SqlClient\SqlError.cs + Microsoft\Data\SqlClient\SqlErrorCollection.cs Microsoft\Data\SqlClient\SqlException.cs + + Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs + Microsoft\Data\SqlClient\SqlInfoMessageEvent.cs Microsoft\Data\SqlClient\SqlInfoMessageEventHandler.cs + + Microsoft\Data\SqlClient\SqlInternalTransaction.cs + Microsoft\Data\SqlClient\SqlMetaDataFactory.cs @@ -406,6 +503,9 @@ Microsoft\Data\SqlClient\SqlParameterCollection.cs + + Microsoft\Data\SqlClient\SqlParameter.cs + Microsoft\Data\SqlClient\SqlQueryMetadataCache.cs @@ -427,98 +527,53 @@ Microsoft\Data\SqlClient\SqlSecurityUtility.cs - - Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs - - - Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs - - - Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs - - - Microsoft\Data\SqlTypes\SQLResource.cs - - - Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs - - - Microsoft\Data\SqlClient\SQLFallbackDNSCache.cs - - - Microsoft\Data\OperationAbortedException.cs - - - Microsoft\Data\DataException.cs + + Microsoft\Data\SqlClient\SqlSequentialStream.cs - - Microsoft\Data\SqlClient\Server\InvalidUdtException.cs + + Microsoft\Data\SqlClient\SqlStream.cs - - Microsoft\Data\SqlClient\Server\SmiTypedGetterSetter.cs + + Microsoft\Data\SqlClient\SqlStatistics.cs - - Microsoft\Data\SqlClient\AlwaysEncryptedAttestationException.cs + + Microsoft\Data\SqlClient\SqlSequentialTextReader.cs - - Microsoft\Data\SqlClient\EnclaveProviderBase.cs + + Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs - - Microsoft\Data\SqlClient\EnclaveSessionCache.cs + + Microsoft\Data\SqlClient\SqlUdtInfo.cs - - Microsoft\Data\SqlClient\SignatureVerificationCache.cs + + Microsoft\Data\SqlClient\SqlUtil.cs - - Microsoft\Data\SqlClient\TdsValueSetter.cs + + Microsoft\Data\SqlClient\TdsEnums.cs Microsoft\Data\SqlClient\TdsParameterSetter.cs + + Microsoft\Data\SqlClient\TdsParserStaticMethods.cs + Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs - - Microsoft\Data\SqlClient\Server\SqlNormalizer.cs - - - Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalBaseEnumerator.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogic.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBase.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicBaseProvider.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryLogicProvider.cs - - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryFactory.cs - - - Microsoft\Data\SqlClient\Reliability\SqlRetryIntervalEnumerators.cs + + Microsoft\Data\SqlClient\TdsValueSetter.cs - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.cs + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProvider.cs - - Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicLoader.cs + + Microsoft\Data\SqlClient\VirtualSecureModeEnclaveProviderBase.cs - - Microsoft\Data\SqlClient\Reliability\AppConfigManager.cs + + Microsoft\Data\SqlTypes\SQLResource.cs - - Microsoft\Data\SqlClient\SqlUtil.cs + + Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs Resources\ResCategoryAttribute.cs @@ -526,33 +581,53 @@ Resources\ResDescriptionAttribute.cs - - Microsoft\Data\SqlClient\SqlConnectionStringBuilder.cs - - - Microsoft\Data\Common\AdapterUtil.cs - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + @@ -561,77 +636,27 @@ - - Microsoft\Data\SqlClient\SqlConnectionTimeoutErrorInternal.cs - - - Microsoft\Data\SqlClient\SqlCredential.cs - - - Microsoft\Data\SqlClient\SqlSequentialStream.cs - - - - - - - - Microsoft\Data\SqlClient\SqlStatistics.cs - - - - Microsoft\Data\SqlClient\SqlUdtInfo.cs - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs index abbfda7ede..13e35363a8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX64.cs @@ -134,5 +134,17 @@ internal static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIClientCertificateFallbackWrapper(IntPtr pCallbackContext); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumOpenWrapper")] + internal static extern IntPtr SNIServerEnumOpen(); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] + internal static extern void SNIServerEnumClose([In] IntPtr packet); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In, Out][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs index b700e4b108..5517ba8c0e 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeManagedWrapperX86.cs @@ -134,5 +134,17 @@ internal static extern unsafe uint SNISecGenClientContextWrapper( [DllImport(SNI, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SNIClientCertificateFallbackWrapper(IntPtr pCallbackContext); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumOpenWrapper")] + internal static extern IntPtr SNIServerEnumOpen(); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumCloseWrapper")] + internal static extern void SNIServerEnumClose([In] IntPtr packet); + + [DllImport(SNI, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SNIServerEnumReadWrapper", CharSet = CharSet.Unicode)] + internal static extern int SNIServerEnumRead([In] IntPtr packet, + [In, Out][MarshalAs(UnmanagedType.LPArray)] char[] readBuffer, + [In] int bufferLength, + [MarshalAs(UnmanagedType.Bool)] out bool more); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs index d1fb0ad3e5..18ca7c68c2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/Interop/SNINativeMethodWrapper.cs @@ -170,14 +170,20 @@ internal struct ConsumerInfo internal IntPtr key; } - + [StructLayout(LayoutKind.Sequential)] internal struct AuthProviderInfo { - internal uint flags; - internal string certId; - internal bool certHash; - internal object clientCertificateCallbackContext; - internal SqlClientCertificateDelegate clientCertificateCallback; + public uint flags; + [MarshalAs(UnmanagedType.Bool)] + public bool tlsFirst; + [MarshalAs(UnmanagedType.LPWStr)] + public string certId; + [MarshalAs(UnmanagedType.Bool)] + public bool certHash; + public object clientCertificateCallbackContext; + public SqlClientCertificateDelegate clientCertificateCallback; + [MarshalAs(UnmanagedType.LPWStr)] + public string serverCertFileName; }; internal struct CTAIPProviderInfo @@ -339,6 +345,8 @@ internal unsafe struct SNI_CLIENT_CONSUMER_INFO public Sni_Consumer_Info ConsumerInfo; [MarshalAs(UnmanagedType.LPWStr)] public string wszConnectionString; + [MarshalAs(UnmanagedType.LPWStr)] + public string HostNameInCertificate; public PrefixEnum networkLibrary; public byte* szSPN; public uint cchSPN; @@ -760,6 +768,26 @@ internal static uint SNIInitialize() return SNIInitialize(LocalAppContextSwitches.UseSystemDefaultSecureProtocols, IntPtr.Zero); } + internal static IntPtr SNIServerEnumOpen() => s_is64bitProcess ? + SNINativeManagedWrapperX64.SNIServerEnumOpen() : + SNINativeManagedWrapperX86.SNIServerEnumOpen(); + + internal static int SNIServerEnumRead([In] IntPtr packet, [In, Out] char[] readbuffer, int bufferLength, out bool more) => s_is64bitProcess ? + SNINativeManagedWrapperX64.SNIServerEnumRead(packet, readbuffer, bufferLength, out more) : + SNINativeManagedWrapperX86.SNIServerEnumRead(packet, readbuffer, bufferLength, out more); + + internal static void SNIServerEnumClose([In] IntPtr packet) + { + if (s_is64bitProcess) + { + SNINativeManagedWrapperX64.SNIServerEnumClose(packet); + } + else + { + SNINativeManagedWrapperX86.SNIServerEnumClose(packet); + } + } + internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHandle parent, ref IntPtr pConn, bool fSync, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) { // initialize consumer info for MARS @@ -775,8 +803,22 @@ internal static unsafe uint SNIOpenMarsSession(ConsumerInfo consumerInfo, SNIHan return SNIOpenWrapper(ref native_consumerInfo, "session:", parent, out pConn, fSync, ipPreference, ref native_cachedDNSInfo); } - internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string constring, ref IntPtr pConn, byte[] spnBuffer, byte[] instanceName, bool fOverrideCache, bool fSync, int timeout, bool fParallel, - Int32 transparentNetworkResolutionStateNo, Int32 totalTimeout, Boolean isAzureSqlServerEndpoint, SqlConnectionIPAddressPreference ipPreference, SQLDNSInfo cachedDNSInfo) + internal static unsafe uint SNIOpenSyncEx( + ConsumerInfo consumerInfo, + string constring, + ref IntPtr pConn, + byte[] spnBuffer, + byte[] instanceName, + bool fOverrideCache, + bool fSync, + int timeout, + bool fParallel, + Int32 transparentNetworkResolutionStateNo, + Int32 totalTimeout, + Boolean isAzureSqlServerEndpoint, + SqlConnectionIPAddressPreference ipPreference, + SQLDNSInfo cachedDNSInfo, + string hostNameInCertificate) { fixed (byte* pin_instanceName = &instanceName[0]) { @@ -786,8 +828,8 @@ internal static unsafe uint SNIOpenSyncEx(ConsumerInfo consumerInfo, string cons MarshalConsumerInfo(consumerInfo, ref clientConsumerInfo.ConsumerInfo); clientConsumerInfo.wszConnectionString = constring; + clientConsumerInfo.HostNameInCertificate = hostNameInCertificate; clientConsumerInfo.networkLibrary = PrefixEnum.UNKNOWN_PREFIX; - clientConsumerInfo.szInstanceName = pin_instanceName; clientConsumerInfo.cchInstanceName = (uint)instanceName.Length; clientConsumerInfo.fOverrideLastConnectCache = fOverrideCache; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs deleted file mode 100644 index cd7c387202..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ /dev/null @@ -1,351 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.ProviderBase -{ - using System.Collections.Concurrent; - using System.Diagnostics; - using Microsoft.Data.Common; - using Microsoft.Data.SqlClient; - - - // set_ConnectionString calls DbConnectionFactory.GetConnectionPoolGroup - // when not found a new pool entry is created and potentially added - // DbConnectionPoolGroup starts in the Active state - - // Open calls DbConnectionFactory.GetConnectionPool - // if the existing pool entry is Disabled, GetConnectionPoolGroup is called for a new entry - // DbConnectionFactory.GetConnectionPool calls DbConnectionPoolGroup.GetConnectionPool - - // DbConnectionPoolGroup.GetConnectionPool will return pool for the current identity - // or null if identity is restricted or pooling is disabled or state is disabled at time of add - // state changes are Active->Active, Idle->Active - - // DbConnectionFactory.PruneConnectionPoolGroups calls Prune - // which will QueuePoolForRelease on all empty pools - // and once no pools remain, change state from Active->Idle->Disabled - // Once Disabled, factory can remove its reference to the pool entry - - sealed internal class DbConnectionPoolGroup - { - private readonly DbConnectionOptions _connectionOptions; - private readonly DbConnectionPoolKey _poolKey; - private readonly DbConnectionPoolGroupOptions _poolGroupOptions; - private ConcurrentDictionary _poolCollection; - - private int _state; // see PoolGroupState* below - - private DbConnectionPoolGroupProviderInfo _providerInfo; - private DbMetaDataFactory _metaDataFactory; - - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - - // always lock this before changing _state, we don't want to move out of the 'Disabled' state - // PoolGroupStateUninitialized = 0; - private const int PoolGroupStateActive = 1; // initial state, GetPoolGroup from cache, connection Open - private const int PoolGroupStateIdle = 2; // all pools are pruned via Clear - private const int PoolGroupStateDisabled = 4; // factory pool entry pruning method - - internal DbConnectionPoolGroup(DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) - { - Debug.Assert(null != connectionOptions, "null connection options"); - Debug.Assert(null == poolGroupOptions || ADP.s_isWindowsNT, "should not have pooling options on Win9x"); - - _connectionOptions = connectionOptions; - _poolKey = key; - _poolGroupOptions = poolGroupOptions; - - // always lock this object before changing state - // HybridDictionary does not create any sub-objects until add - // so it is safe to use for non-pooled connection as long as - // we check _poolGroupOptions first - _poolCollection = new ConcurrentDictionary(); - _state = PoolGroupStateActive; // VSWhidbey 112102 - } - - internal DbConnectionOptions ConnectionOptions - { - get - { - return _connectionOptions; - } - } - - internal DbConnectionPoolKey PoolKey - { - get - { - return _poolKey; - } - } - - internal DbConnectionPoolGroupProviderInfo ProviderInfo - { - get - { - return _providerInfo; - } - set - { - _providerInfo = value; - if (null != value) - { - _providerInfo.PoolGroup = this; - } - } - } - - internal bool IsDisabled - { - get - { - return (PoolGroupStateDisabled == _state); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal DbConnectionPoolGroupOptions PoolGroupOptions - { - get - { - return _poolGroupOptions; - } - } - - internal DbMetaDataFactory MetaDataFactory - { - get - { - return _metaDataFactory; - } - - set - { - _metaDataFactory = value; - } - } - - internal int Clear() - { - // must be multi-thread safe with competing calls by Clear and Prune via background thread - // will return the number of connections in the group after clearing has finished - - // First, note the old collection and create a new collection to be used - ConcurrentDictionary oldPoolCollection = null; - lock (this) - { - if (_poolCollection.Count > 0) - { - oldPoolCollection = _poolCollection; - _poolCollection = new ConcurrentDictionary(); - } - } - - // Then, if a new collection was created, release the pools from the old collection - if (oldPoolCollection != null) - { - foreach (var entry in oldPoolCollection) - { - DbConnectionPool pool = entry.Value; - if (pool != null) - { - // TODO: SQLBU 422890 - // Pruning a pool while a connection is currently attempting to connect - // will cause the pool to be prematurely abandoned. The only known effect so - // far is that the errorWait throttling will be reset when this occurs. - // We should be able to avoid this situation by not pruning the pool if - // it's _waitCount is non-zero (i.e. no connections *in* the pool, but also - // no connections attempting to be created for the pool). - - DbConnectionFactory connectionFactory = pool.ConnectionFactory; - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); - connectionFactory.QueuePoolForRelease(pool, true); - } - } - } - - // Finally, return the pool collection count - this may be non-zero if something was added while we were clearing - return _poolCollection.Count; - } - - internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactory) - { - // When this method returns null it indicates that the connection - // factory should not use pooling. - - // We don't support connection pooling on Win9x; it lacks too - // many of the APIs we require. - // PoolGroupOptions will only be null when we're not supposed to pool - // connections. - DbConnectionPool pool = null; - if (null != _poolGroupOptions) - { - Debug.Assert(ADP.s_isWindowsNT, "should not be pooling on Win9x"); - - DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; - if (_poolGroupOptions.PoolByIdentity) - { - // if we're pooling by identity (because integrated security is - // being used for these connections) then we need to go out and - // search for the connectionPool that matches the current identity. - - currentIdentity = DbConnectionPoolIdentity.GetCurrent(); - - // If the current token is restricted in some way, then we must - // not attempt to pool these connections. - if (currentIdentity.IsRestricted) - { - currentIdentity = null; - } - } - - if (null != currentIdentity) - { - if (!_poolCollection.TryGetValue(currentIdentity, out pool)) - { // find the pool - - - lock (this) - { - // Did someone already add it to the list? - if (!_poolCollection.TryGetValue(currentIdentity, out pool)) - { - DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions); - DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); - - if (MarkPoolGroupAsActive()) - { - // If we get here, we know for certain that we there isn't - // a pool that matches the current identity, so we have to - // add the optimistically created one - newPool.Startup(); // must start pool before usage - bool addResult = _poolCollection.TryAdd(currentIdentity, newPool); - Debug.Assert(addResult, "No other pool with current identity should exist at this point"); - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment(); - pool = newPool; - } - else - { - // else pool entry has been disabled so don't create new pools - Debug.Assert(PoolGroupStateDisabled == _state, "state should be disabled"); - - // don't need to call connectionFactory.QueuePoolForRelease(newPool) because - // pool callbacks were delayed and no risk of connections being created - newPool.Shutdown(); - } - } - else - { - // else found an existing pool to use instead - Debug.Assert(PoolGroupStateActive == _state, "state should be active since a pool exists and lock holds"); - } - } - } - // the found pool could be in any state - } - } - - if (null == pool) - { - lock (this) - { - // keep the pool entry state active when not pooling - MarkPoolGroupAsActive(); - } - } - return pool; - } - - private bool MarkPoolGroupAsActive() - { - // when getting a connection, make the entry active if it was idle (but not disabled) - // must always lock this before calling - - if (PoolGroupStateIdle == _state) - { - _state = PoolGroupStateActive; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Active", ObjectID); - } - return (PoolGroupStateActive == _state); - } - - internal bool Prune() - { - // must only call from DbConnectionFactory.PruneConnectionPoolGroups on background timer thread - // must lock(DbConnectionFactory._connectionPoolGroups.SyncRoot) before calling ReadyToRemove - // to avoid conflict with DbConnectionFactory.CreateConnectionPoolGroup replacing pool entry - lock (this) - { - if (_poolCollection.Count > 0) - { - var newPoolCollection = new ConcurrentDictionary(); - - foreach (var entry in _poolCollection) - { - DbConnectionPool pool = entry.Value; - if (pool != null) - { - // TODO: SQLBU 422890 - // Pruning a pool while a connection is currently attempting to connect - // will cause the pool to be prematurely abandoned. The only known effect so - // far is that the errorWait throttling will be reset when this occurs. - // We should be able to avoid this situation by not pruning the pool if - // it's _waitCount is non-zero (i.e. no connections *in* the pool, but also - // no connections attempting to be created for the pool). - - // Actually prune the pool if there are no connections in the pool and no errors occurred. - // Empty pool during pruning indicates zero or low activity, but - // an error state indicates the pool needs to stay around to - // throttle new connection attempts. - if ((!pool.ErrorOccurred) && (0 == pool.Count)) - { - - // Order is important here. First we remove the pool - // from the collection of pools so no one will try - // to use it while we're processing and finally we put the - // pool into a list of pools to be released when they - // are completely empty. - DbConnectionFactory connectionFactory = pool.ConnectionFactory; - - connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); - connectionFactory.QueuePoolForRelease(pool, false); - } - else - { - newPoolCollection.TryAdd(entry.Key, entry.Value); - } - } - } - _poolCollection = newPoolCollection; - } - - // must be pruning thread to change state and no connections - // otherwise pruning thread risks making entry disabled soon after user calls ClearPool - if (0 == _poolCollection.Count) - { - if (PoolGroupStateActive == _state) - { - _state = PoolGroupStateIdle; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Idle", ObjectID); - } - else if (PoolGroupStateIdle == _state) - { - _state = PoolGroupStateDisabled; - SqlClientEventSource.Log.TryTraceEvent(" {0}, Disabled", ObjectID); - } - } - - return (PoolGroupStateDisabled == _state); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs index f1f4c955af..e3b4ea2ef4 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/LocalDBAPI.cs @@ -16,11 +16,11 @@ namespace Microsoft.Data { - internal static class LocalDBAPI { - const string const_localDbPrefix = @"(localdb)\"; - const string const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST"; + private const string LocalDbPrefix = @"(localdb)\"; + private const string LocalDbPrefix_NP = @"np:\\.\pipe\LOCALDB#"; + const string Const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST"; static PermissionSet _fullTrust = null; static bool _partialTrustFlagChecked = false; @@ -30,18 +30,27 @@ internal static class LocalDBAPI // check if name is in format (localdb)\ and return instance name if it is internal static string GetLocalDbInstanceNameFromServerName(string serverName) { - if (serverName == null) - return null; - serverName = serverName.TrimStart(); // it can start with spaces if specified in quotes - if (!serverName.StartsWith(const_localDbPrefix, StringComparison.OrdinalIgnoreCase)) - return null; - string instanceName = serverName.Substring(const_localDbPrefix.Length).Trim(); - if (instanceName.Length == 0) - return null; - else - return instanceName; - } + if (serverName is not null) + { + // it can start with spaces if specified in quotes + // Memory allocation is reduced by using ReadOnlySpan + ReadOnlySpan input = serverName.AsSpan().Trim(); + if (input.StartsWith(LocalDbPrefix.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + input = input.Slice(LocalDbPrefix.Length); + if (!input.IsEmpty) + { + return input.ToString(); + } + } + else if (input.StartsWith(LocalDbPrefix_NP.AsSpan(), StringComparison.OrdinalIgnoreCase)) + { + return input.ToString(); + } + } + return null; + } internal static void ReleaseDLLHandles() { @@ -261,7 +270,7 @@ internal static void DemandLocalDBPermissions() { if (!_partialTrustFlagChecked) { - object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(const_partialTrustFlagKey); + object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(Const_partialTrustFlagKey); if (partialTrustFlagValue != null && partialTrustFlagValue is bool) { _partialTrustAllowed = (bool)partialTrustFlagValue; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs deleted file mode 100644 index 92baf05ff2..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs +++ /dev/null @@ -1,1067 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Data; -using System.Data.Common; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient.Server -{ - - // Utilities for manipulating smi-related metadata. - // - // THIS CLASS IS BUILT ON TOP OF THE SMI INTERFACE -- SMI SHOULD NOT DEPEND ON IT! - // - // These are all based off of knowing the clr type of the value - // as an ExtendedClrTypeCode enum for rapid access (lookup in static array is best, if possible). - internal class MetaDataUtilsSmi - { - - internal const SqlDbType InvalidSqlDbType = (SqlDbType)(-1); - internal const long InvalidMaxLength = -2; - - // Standard type inference map to get SqlDbType when all you know is the value's type (typecode) - // This map's index is off by one (add one to typecode locate correct entry) in order - // support ExtendedSqlDbType.Invalid - // ONLY ACCESS THIS ARRAY FROM InferSqlDbTypeFromTypeCode!!! - static readonly SqlDbType[] __extendedTypeCodeToSqlDbTypeMap = { - InvalidSqlDbType, // Invalid extended type code - SqlDbType.Bit, // System.Boolean - SqlDbType.TinyInt, // System.Byte - SqlDbType.NVarChar, // System.Char - SqlDbType.DateTime, // System.DateTime - InvalidSqlDbType, // System.DBNull doesn't have an inferable SqlDbType - SqlDbType.Decimal, // System.Decimal - SqlDbType.Float, // System.Double - InvalidSqlDbType, // null reference doesn't have an inferable SqlDbType - SqlDbType.SmallInt, // System.Int16 - SqlDbType.Int, // System.Int32 - SqlDbType.BigInt, // System.Int64 - InvalidSqlDbType, // System.SByte doesn't have an inferable SqlDbType - SqlDbType.Real, // System.Single - SqlDbType.NVarChar, // System.String - InvalidSqlDbType, // System.UInt16 doesn't have an inferable SqlDbType - InvalidSqlDbType, // System.UInt32 doesn't have an inferable SqlDbType - InvalidSqlDbType, // System.UInt64 doesn't have an inferable SqlDbType - InvalidSqlDbType, // System.Object doesn't have an inferable SqlDbType - SqlDbType.VarBinary, // System.ByteArray - SqlDbType.NVarChar, // System.CharArray - SqlDbType.UniqueIdentifier, // System.Guid - SqlDbType.VarBinary, // System.Data.SqlTypes.SqlBinary - SqlDbType.Bit, // System.Data.SqlTypes.SqlBoolean - SqlDbType.TinyInt, // System.Data.SqlTypes.SqlByte - SqlDbType.DateTime, // System.Data.SqlTypes.SqlDateTime - SqlDbType.Float, // System.Data.SqlTypes.SqlDouble - SqlDbType.UniqueIdentifier, // System.Data.SqlTypes.SqlGuid - SqlDbType.SmallInt, // System.Data.SqlTypes.SqlInt16 - SqlDbType.Int, // System.Data.SqlTypes.SqlInt32 - SqlDbType.BigInt, // System.Data.SqlTypes.SqlInt64 - SqlDbType.Money, // System.Data.SqlTypes.SqlMoney - SqlDbType.Decimal, // System.Data.SqlTypes.SqlDecimal - SqlDbType.Real, // System.Data.SqlTypes.SqlSingle - SqlDbType.NVarChar, // System.Data.SqlTypes.SqlString - SqlDbType.NVarChar, // System.Data.SqlTypes.SqlChars - SqlDbType.VarBinary, // System.Data.SqlTypes.SqlBytes - SqlDbType.Xml, // System.Data.SqlTypes.SqlXml - SqlDbType.Structured, // Microsoft.Data.DataTable - SqlDbType.Structured, // System.Collections.IEnumerable, used for TVPs it must return IDataRecord - SqlDbType.Structured, // System.Collections.Generic.IEnumerable - SqlDbType.Time, // System.TimeSpan - SqlDbType.DateTimeOffset, // System.DateTimeOffset - }; - - // Hash table to map from clr type object to ExtendedClrTypeCodeMap enum - // ONLY ACCESS THIS HASH TABLE FROM DetermineExtendedTypeCode METHOD!!! (and class ctor for setup) - static readonly Hashtable __typeToExtendedTypeCodeMap; - - - // class ctor - static MetaDataUtilsSmi() - { - // set up type mapping hash table - // keep this initialization list in the same order as ExtendedClrTypeCode for ease in validating! - Hashtable ht = new Hashtable(42); - ht.Add(typeof(System.Boolean), ExtendedClrTypeCode.Boolean); - ht.Add(typeof(System.Byte), ExtendedClrTypeCode.Byte); - ht.Add(typeof(System.Char), ExtendedClrTypeCode.Char); - ht.Add(typeof(System.DateTime), ExtendedClrTypeCode.DateTime); - ht.Add(typeof(System.DBNull), ExtendedClrTypeCode.DBNull); - ht.Add(typeof(System.Decimal), ExtendedClrTypeCode.Decimal); - ht.Add(typeof(System.Double), ExtendedClrTypeCode.Double); - // lookup code will have to special-case null-ref anyway, so don't bother adding ExtendedTypeCode.Empty to the table - ht.Add(typeof(System.Int16), ExtendedClrTypeCode.Int16); - ht.Add(typeof(System.Int32), ExtendedClrTypeCode.Int32); - ht.Add(typeof(System.Int64), ExtendedClrTypeCode.Int64); - ht.Add(typeof(System.SByte), ExtendedClrTypeCode.SByte); - ht.Add(typeof(System.Single), ExtendedClrTypeCode.Single); - ht.Add(typeof(System.String), ExtendedClrTypeCode.String); - ht.Add(typeof(System.UInt16), ExtendedClrTypeCode.UInt16); - ht.Add(typeof(System.UInt32), ExtendedClrTypeCode.UInt32); - ht.Add(typeof(System.UInt64), ExtendedClrTypeCode.UInt64); - ht.Add(typeof(System.Object), ExtendedClrTypeCode.Object); - ht.Add(typeof(System.Byte[]), ExtendedClrTypeCode.ByteArray); - ht.Add(typeof(System.Char[]), ExtendedClrTypeCode.CharArray); - ht.Add(typeof(System.Guid), ExtendedClrTypeCode.Guid); - ht.Add(typeof(SqlBinary), ExtendedClrTypeCode.SqlBinary); - ht.Add(typeof(SqlBoolean), ExtendedClrTypeCode.SqlBoolean); - ht.Add(typeof(SqlByte), ExtendedClrTypeCode.SqlByte); - ht.Add(typeof(SqlDateTime), ExtendedClrTypeCode.SqlDateTime); - ht.Add(typeof(SqlDouble), ExtendedClrTypeCode.SqlDouble); - ht.Add(typeof(SqlGuid), ExtendedClrTypeCode.SqlGuid); - ht.Add(typeof(SqlInt16), ExtendedClrTypeCode.SqlInt16); - ht.Add(typeof(SqlInt32), ExtendedClrTypeCode.SqlInt32); - ht.Add(typeof(SqlInt64), ExtendedClrTypeCode.SqlInt64); - ht.Add(typeof(SqlMoney), ExtendedClrTypeCode.SqlMoney); - ht.Add(typeof(SqlDecimal), ExtendedClrTypeCode.SqlDecimal); - ht.Add(typeof(SqlSingle), ExtendedClrTypeCode.SqlSingle); - ht.Add(typeof(SqlString), ExtendedClrTypeCode.SqlString); - ht.Add(typeof(SqlChars), ExtendedClrTypeCode.SqlChars); - ht.Add(typeof(SqlBytes), ExtendedClrTypeCode.SqlBytes); - ht.Add(typeof(SqlXml), ExtendedClrTypeCode.SqlXml); - ht.Add(typeof(DataTable), ExtendedClrTypeCode.DataTable); - ht.Add(typeof(DbDataReader), ExtendedClrTypeCode.DbDataReader); - ht.Add(typeof(IEnumerable), ExtendedClrTypeCode.IEnumerableOfSqlDataRecord); - ht.Add(typeof(System.TimeSpan), ExtendedClrTypeCode.TimeSpan); - ht.Add(typeof(System.DateTimeOffset), ExtendedClrTypeCode.DateTimeOffset); - __typeToExtendedTypeCodeMap = ht; - } - - - internal static bool IsCharOrXmlType(SqlDbType type) - { - return IsUnicodeType(type) || - IsAnsiType(type) || - type == SqlDbType.Xml; - } - - internal static bool IsUnicodeType(SqlDbType type) - { - return type == SqlDbType.NChar || - type == SqlDbType.NVarChar || - type == SqlDbType.NText; - } - - internal static bool IsAnsiType(SqlDbType type) - { - return type == SqlDbType.Char || - type == SqlDbType.VarChar || - type == SqlDbType.Text; - } - - internal static bool IsBinaryType(SqlDbType type) - { - return type == SqlDbType.Binary || - type == SqlDbType.VarBinary || - type == SqlDbType.Image; - } - - // Does this type use PLP format values? - internal static bool IsPlpFormat(SmiMetaData metaData) - { - return metaData.MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator || - metaData.SqlDbType == SqlDbType.Image || - metaData.SqlDbType == SqlDbType.NText || - metaData.SqlDbType == SqlDbType.Text || - metaData.SqlDbType == SqlDbType.Udt; - } - - - - // If we know we're only going to use this object to assign to a specific SqlDbType back end object, - // we can save some processing time by only checking for the few valid types that can be assigned to the dbType. - // This assumes a switch statement over SqlDbType is faster than getting the ClrTypeCode and iterating over a - // series of if statements, or using a hash table. - // NOTE: the form of these checks is taking advantage of a feature of the JIT compiler that is supposed to - // optimize checks of the form '(xxx.GetType() == typeof( YYY ))'. The JIT team claimed at one point that - // this doesn't even instantiate a Type instance, thus was the fastest method for individual comparisons. - // Given that there's a known SqlDbType, thus a minimal number of comparisons, it's likely this is faster - // than the other approaches considered (both GetType().GetTypeCode() switch and hash table using Type keys - // must instantiate a Type object. The typecode switch also degenerates into a large if-then-else for - // all but the primitive clr types. - internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType( - SqlDbType dbType, - bool isMultiValued, - object value, - Type udtType, - ulong smiVersion) - { - ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid; - - // fast-track null, which is valid for all types - if (null == value) - { - extendedCode = ExtendedClrTypeCode.Empty; - } - else if (DBNull.Value == value) - { - extendedCode = ExtendedClrTypeCode.DBNull; - } - else - { - switch (dbType) - { - case SqlDbType.BigInt: - if (value.GetType() == typeof(Int64)) - extendedCode = ExtendedClrTypeCode.Int64; - else if (value.GetType() == typeof(SqlInt64)) - extendedCode = ExtendedClrTypeCode.SqlInt64; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int64) - extendedCode = ExtendedClrTypeCode.Int64; - break; - case SqlDbType.Binary: - case SqlDbType.VarBinary: - case SqlDbType.Image: - case SqlDbType.Timestamp: - if (value.GetType() == typeof(byte[])) - extendedCode = ExtendedClrTypeCode.ByteArray; - else if (value.GetType() == typeof(SqlBinary)) - extendedCode = ExtendedClrTypeCode.SqlBinary; - else if (value.GetType() == typeof(SqlBytes)) - extendedCode = ExtendedClrTypeCode.SqlBytes; - else if (value.GetType() == typeof(StreamDataFeed)) - extendedCode = ExtendedClrTypeCode.Stream; - break; - case SqlDbType.Bit: - if (value.GetType() == typeof(bool)) - extendedCode = ExtendedClrTypeCode.Boolean; - else if (value.GetType() == typeof(SqlBoolean)) - extendedCode = ExtendedClrTypeCode.SqlBoolean; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Boolean) - extendedCode = ExtendedClrTypeCode.Boolean; - break; - case SqlDbType.Char: - case SqlDbType.NChar: - case SqlDbType.NText: - case SqlDbType.NVarChar: - case SqlDbType.Text: - case SqlDbType.VarChar: - if (value.GetType() == typeof(string)) - extendedCode = ExtendedClrTypeCode.String; - if (value.GetType() == typeof(TextDataFeed)) - extendedCode = ExtendedClrTypeCode.TextReader; - else if (value.GetType() == typeof(SqlString)) - extendedCode = ExtendedClrTypeCode.SqlString; - else if (value.GetType() == typeof(char[])) - extendedCode = ExtendedClrTypeCode.CharArray; - else if (value.GetType() == typeof(SqlChars)) - extendedCode = ExtendedClrTypeCode.SqlChars; - else if (value.GetType() == typeof(char)) - extendedCode = ExtendedClrTypeCode.Char; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Char) - extendedCode = ExtendedClrTypeCode.Char; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.String) - extendedCode = ExtendedClrTypeCode.String; - break; - case SqlDbType.Date: - case SqlDbType.DateTime2: - if (smiVersion >= SmiContextFactory.Sql2008Version) - { - goto case SqlDbType.DateTime; - } - break; - case SqlDbType.DateTime: - case SqlDbType.SmallDateTime: - if (value.GetType() == typeof(DateTime)) - extendedCode = ExtendedClrTypeCode.DateTime; - else if (value.GetType() == typeof(SqlDateTime)) - extendedCode = ExtendedClrTypeCode.SqlDateTime; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.DateTime) - extendedCode = ExtendedClrTypeCode.DateTime; - break; - case SqlDbType.Decimal: - if (value.GetType() == typeof(Decimal)) - extendedCode = ExtendedClrTypeCode.Decimal; - else if (value.GetType() == typeof(SqlDecimal)) - extendedCode = ExtendedClrTypeCode.SqlDecimal; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) - extendedCode = ExtendedClrTypeCode.Decimal; - break; - case SqlDbType.Real: - if (value.GetType() == typeof(Single)) - extendedCode = ExtendedClrTypeCode.Single; - else if (value.GetType() == typeof(SqlSingle)) - extendedCode = ExtendedClrTypeCode.SqlSingle; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Single) - extendedCode = ExtendedClrTypeCode.Single; - break; - case SqlDbType.Int: - if (value.GetType() == typeof(Int32)) - extendedCode = ExtendedClrTypeCode.Int32; - else if (value.GetType() == typeof(SqlInt32)) - extendedCode = ExtendedClrTypeCode.SqlInt32; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int32) - extendedCode = ExtendedClrTypeCode.Int32; - break; - case SqlDbType.Money: - case SqlDbType.SmallMoney: - if (value.GetType() == typeof(SqlMoney)) - extendedCode = ExtendedClrTypeCode.SqlMoney; - else if (value.GetType() == typeof(Decimal)) - extendedCode = ExtendedClrTypeCode.Decimal; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) - extendedCode = ExtendedClrTypeCode.Decimal; - break; - case SqlDbType.Float: - if (value.GetType() == typeof(SqlDouble)) - extendedCode = ExtendedClrTypeCode.SqlDouble; - else if (value.GetType() == typeof(Double)) - extendedCode = ExtendedClrTypeCode.Double; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Double) - extendedCode = ExtendedClrTypeCode.Double; - break; - case SqlDbType.UniqueIdentifier: - if (value.GetType() == typeof(SqlGuid)) - extendedCode = ExtendedClrTypeCode.SqlGuid; - else if (value.GetType() == typeof(Guid)) - extendedCode = ExtendedClrTypeCode.Guid; - break; - case SqlDbType.SmallInt: - if (value.GetType() == typeof(Int16)) - extendedCode = ExtendedClrTypeCode.Int16; - else if (value.GetType() == typeof(SqlInt16)) - extendedCode = ExtendedClrTypeCode.SqlInt16; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int16) - extendedCode = ExtendedClrTypeCode.Int16; - break; - case SqlDbType.TinyInt: - if (value.GetType() == typeof(Byte)) - extendedCode = ExtendedClrTypeCode.Byte; - else if (value.GetType() == typeof(SqlByte)) - extendedCode = ExtendedClrTypeCode.SqlByte; - else if (Type.GetTypeCode(value.GetType()) == TypeCode.Byte) - extendedCode = ExtendedClrTypeCode.Byte; - break; - case SqlDbType.Variant: - // SqlDbType doesn't help us here, call general-purpose function - extendedCode = DetermineExtendedTypeCode(value); - - // Some types aren't allowed for Variants but are for the general-purpose function. - // Match behavior of other types and return invalid in these cases. - if (ExtendedClrTypeCode.SqlXml == extendedCode) - { - extendedCode = ExtendedClrTypeCode.Invalid; - } - break; - case SqlDbType.Udt: - // Validate UDT type if caller gave us a type to validate against - if (null == udtType || - value.GetType() == udtType - ) - { - extendedCode = ExtendedClrTypeCode.Object; - } - else - { - extendedCode = ExtendedClrTypeCode.Invalid; - } - break; - case SqlDbType.Time: - if (value.GetType() == typeof(TimeSpan) && smiVersion >= SmiContextFactory.Sql2008Version) - extendedCode = ExtendedClrTypeCode.TimeSpan; - break; - case SqlDbType.DateTimeOffset: - if (value.GetType() == typeof(DateTimeOffset) && smiVersion >= SmiContextFactory.Sql2008Version) - extendedCode = ExtendedClrTypeCode.DateTimeOffset; - break; - case SqlDbType.Xml: - if (value.GetType() == typeof(SqlXml)) - extendedCode = ExtendedClrTypeCode.SqlXml; - if (value.GetType() == typeof(XmlDataFeed)) - extendedCode = ExtendedClrTypeCode.XmlReader; - else if (value.GetType() == typeof(System.String)) - extendedCode = ExtendedClrTypeCode.String; - break; - case SqlDbType.Structured: - if (isMultiValued) - { - if (value is DataTable) - { - extendedCode = ExtendedClrTypeCode.DataTable; - } - // Order is important, since some of these types are base types of the others. - // Evaluate from most derived to parent types - else if (value is IEnumerable) - { - extendedCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; - } - else if (value is DbDataReader) - { - extendedCode = ExtendedClrTypeCode.DbDataReader; - } - } - break; - default: - // Leave as invalid - break; - } - } - - return extendedCode; - - } - - // Method to map from Type to ExtendedTypeCode - static internal ExtendedClrTypeCode DetermineExtendedTypeCodeFromType(Type clrType) - { - object result = __typeToExtendedTypeCodeMap[clrType]; - - ExtendedClrTypeCode resultCode; - if (null == result) - { - resultCode = ExtendedClrTypeCode.Invalid; - } - else - { - resultCode = (ExtendedClrTypeCode)result; - } - - return resultCode; - } - - // Returns the ExtendedClrTypeCode that describes the given value - // UNDONE: see if switch on Type.GetTypeCode() + conditionals for object types would be even faster than hash table. - // something like: - // Type type = value.GetType(); - // switch( type.GetTypeCode() ) { - // case Int16: resultCode = ExtendedClrTypeCode.Int16; break; - // ... - // case Object: { - // if ( type == typeof( byte[] ) { - // resultCode = ExtendedClrTypeCode.ByteArray; - // } - // else if ( type == typeof( char[] ) { - // ... - static internal ExtendedClrTypeCode DetermineExtendedTypeCode(object value) - { - ExtendedClrTypeCode resultCode; - if (null == value) - { - resultCode = ExtendedClrTypeCode.Empty; - } - else - { - resultCode = DetermineExtendedTypeCodeFromType(value.GetType()); - } - - return resultCode; - } - - // returns a sqldbtype for the given type code - static internal SqlDbType InferSqlDbTypeFromTypeCode(ExtendedClrTypeCode typeCode) - { - Debug.Assert(typeCode >= ExtendedClrTypeCode.Invalid && typeCode <= ExtendedClrTypeCode.Last, "Someone added a typecode without adding support here!"); - - return __extendedTypeCodeToSqlDbTypeMap[(int)typeCode + 1]; - } - - // Infer SqlDbType from Type in the general case. 2008-only (or later) features that need to - // infer types should use InferSqlDbTypeFromType_2008. - static internal SqlDbType InferSqlDbTypeFromType(Type type) - { - ExtendedClrTypeCode typeCode = DetermineExtendedTypeCodeFromType(type); - SqlDbType returnType; - if (ExtendedClrTypeCode.Invalid == typeCode) - { - returnType = InvalidSqlDbType; // Return invalid type so caller can generate specific error - } - else - { - returnType = InferSqlDbTypeFromTypeCode(typeCode); - } - - return returnType; - } - - // Inference rules changed for 2008-or-later-only cases. Only features that are guaranteed to be - // running against 2008 and don't have backward compat issues should call this code path. - // example: TVP's are a new 2008 feature (no back compat issues) so can infer DATETIME2 - // when mapping System.DateTime from DateTable or DbDataReader. DATETIME2 is better because - // of greater range that can handle all DateTime values. - static internal SqlDbType InferSqlDbTypeFromType_2008(Type type) - { - SqlDbType returnType = InferSqlDbTypeFromType(type); - if (SqlDbType.DateTime == returnType) - { - returnType = SqlDbType.DateTime2; - } - return returnType; - } - - static internal bool IsValidForSmiVersion(SmiExtendedMetaData md, ulong smiVersion) - { - if (SmiContextFactory.LatestVersion == smiVersion) - { - return true; - } - else - { - // 2005 doesn't support Structured nor the new time types - Debug.Assert(SmiContextFactory.Sql2005Version == smiVersion, "Other versions should have been eliminated during link stage"); - return md.SqlDbType != SqlDbType.Structured && - md.SqlDbType != SqlDbType.Date && - md.SqlDbType != SqlDbType.DateTime2 && - md.SqlDbType != SqlDbType.DateTimeOffset && - md.SqlDbType != SqlDbType.Time; - } - } - - static internal SqlMetaData SmiExtendedMetaDataToSqlMetaData(SmiExtendedMetaData source) - { - SqlMetaData sqlMetaDataInstance = (SqlMetaData)Activator.CreateInstance(typeof(SqlMetaData), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, - null, new object[] {source.Name, source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - source.TypeSpecificNamePart1, - source.TypeSpecificNamePart2, - source.TypeSpecificNamePart3, - true, - source.Type } - , null); - - if (SqlDbType.Xml == source.SqlDbType) - { - return sqlMetaDataInstance; - } - - return new SqlMetaData(source.Name, - source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - source.Type); - } - - // Convert SqlMetaData instance to an SmiExtendedMetaData instance. - - internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData(SqlMetaData source) - { - // now map everything across to the extended metadata object - string typeSpecificNamePart1 = null; - string typeSpecificNamePart2 = null; - string typeSpecificNamePart3 = null; - - if (SqlDbType.Xml == source.SqlDbType) - { - typeSpecificNamePart1 = source.XmlSchemaCollectionDatabase; - typeSpecificNamePart2 = source.XmlSchemaCollectionOwningSchema; - typeSpecificNamePart3 = source.XmlSchemaCollectionName; - } - else if (SqlDbType.Udt == source.SqlDbType) - { - // Split the input name. UdtTypeName is specified as single 3 part name. - // NOTE: ParseUdtTypeName throws if format is incorrect - PropertyInfo serverTypeNameProperty = source.GetType().GetProperty("ServerTypeName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = serverTypeNameProperty.GetGetMethod(nonPublic: true); - String typeName = (string)getter.Invoke(source, null); - if (null != typeName) - { - String[] names = SqlParameter.ParseTypeName(typeName, true /* is for UdtTypeName */); - - if (1 == names.Length) - { - typeSpecificNamePart3 = names[0]; - } - else if (2 == names.Length) - { - typeSpecificNamePart2 = names[0]; - typeSpecificNamePart3 = names[1]; - } - else if (3 == names.Length) - { - typeSpecificNamePart1 = names[0]; - typeSpecificNamePart2 = names[1]; - typeSpecificNamePart3 = names[2]; - } - else - { - throw ADP.ArgumentOutOfRange("typeName"); - } - - if ((!ADP.IsEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) - || (!ADP.IsEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length) - || (!ADP.IsEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length)) - { - throw ADP.ArgumentOutOfRange("typeName"); - } - } - } - - return new SmiExtendedMetaData(source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - source.Type, - source.Name, - typeSpecificNamePart1, - typeSpecificNamePart2, - typeSpecificNamePart3); - - - } - - - // compare SmiMetaData to SqlMetaData and determine if they are compatible. - static internal bool IsCompatible(SmiMetaData firstMd, SqlMetaData secondMd) - { - return firstMd.SqlDbType == secondMd.SqlDbType && - firstMd.MaxLength == secondMd.MaxLength && - firstMd.Precision == secondMd.Precision && - firstMd.Scale == secondMd.Scale && - firstMd.CompareOptions == secondMd.CompareOptions && - firstMd.LocaleId == secondMd.LocaleId && - firstMd.Type == secondMd.Type && - firstMd.SqlDbType != SqlDbType.Structured && // SqlMetaData doesn't support Structured types - !firstMd.IsMultiValued; // SqlMetaData doesn't have a "multivalued" option - } - - static internal long AdjustMaxLength(SqlDbType dbType, long maxLength) - { - if (SmiMetaData.UnlimitedMaxLengthIndicator != maxLength) - { - if (maxLength < 0) - { - maxLength = InvalidMaxLength; - } - - switch (dbType) - { - case SqlDbType.Binary: - if (maxLength > SmiMetaData.MaxBinaryLength) - { - maxLength = InvalidMaxLength; - } - break; - case SqlDbType.Char: - if (maxLength > SmiMetaData.MaxANSICharacters) - { - maxLength = InvalidMaxLength; - } - break; - case SqlDbType.NChar: - if (maxLength > SmiMetaData.MaxUnicodeCharacters) - { - maxLength = InvalidMaxLength; - } - break; - case SqlDbType.NVarChar: - // Promote to MAX type if it won't fit in a normal type - if (SmiMetaData.MaxUnicodeCharacters < maxLength) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - break; - case SqlDbType.VarBinary: - // Promote to MAX type if it won't fit in a normal type - if (SmiMetaData.MaxBinaryLength < maxLength) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - break; - case SqlDbType.VarChar: - // Promote to MAX type if it won't fit in a normal type - if (SmiMetaData.MaxANSICharacters < maxLength) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - break; - default: - break; - } - } - - return maxLength; - } - - private static CultureInfo GetColumnLocale(DataColumn column) - { - PropertyInfo localeProperty = column.GetType().GetProperty("Locale", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = localeProperty.GetGetMethod(nonPublic: true); - return (CultureInfo)getter.Invoke(column, null); - } - - // Extract metadata for a single DataColumn - static internal SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, DataTable parent) - { - SqlDbType dbType = InferSqlDbTypeFromType_2008(column.DataType); - if (InvalidSqlDbType == dbType) - { - throw SQL.UnsupportedColumnTypeForSqlProvider(column.ColumnName, column.DataType.Name); - } - - long maxLength = AdjustMaxLength(dbType, column.MaxLength); - if (InvalidMaxLength == maxLength) - { - throw SQL.InvalidColumnMaxLength(column.ColumnName, maxLength); - } - - byte precision; - byte scale; - if (column.DataType == typeof(SqlDecimal)) - { - - // Must scan all values in column to determine best-fit precision & scale - Debug.Assert(null != parent); - scale = 0; - byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision - foreach (DataRow row in parent.Rows) - { - object obj = row[column]; - if (!(obj is DBNull)) - { - SqlDecimal value = (SqlDecimal)obj; - if (!value.IsNull) - { - byte tempNonFractPrec = checked((byte)(value.Precision - value.Scale)); - if (tempNonFractPrec > nonFractionalPrecision) - { - nonFractionalPrecision = tempNonFractPrec; - } - - if (value.Scale > scale) - { - scale = value.Scale; - } - } - } - } - - precision = checked((byte)(nonFractionalPrecision + scale)); - - if (SqlDecimal.MaxPrecision < precision) - { - throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision); - } - else if (0 == precision) - { - precision = 1; - } - } - else if (dbType == SqlDbType.DateTime2 || dbType == SqlDbType.DateTimeOffset || dbType == SqlDbType.Time) - { - // Time types care about scale, too. But have to infer maximums for these. - precision = 0; - scale = SmiMetaData.DefaultTime.Scale; - } - else if (dbType == SqlDbType.Decimal) - { - // Must scan all values in column to determine best-fit precision & scale - Debug.Assert(null != parent); - scale = 0; - byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision - foreach (DataRow row in parent.Rows) - { - object obj = row[column]; - if (!(obj is DBNull)) - { - SqlDecimal value = (SqlDecimal)(Decimal)obj; - byte tempNonFractPrec = checked((byte)(value.Precision - value.Scale)); - if (tempNonFractPrec > nonFractionalPrecision) - { - nonFractionalPrecision = tempNonFractPrec; - } - - if (value.Scale > scale) - { - scale = value.Scale; - } - } - } - - precision = checked((byte)(nonFractionalPrecision + scale)); - - if (SqlDecimal.MaxPrecision < precision) - { - throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision); - } - else if (0 == precision) - { - precision = 1; - } - } - else - { - precision = 0; - scale = 0; - } - - CultureInfo locale = GetColumnLocale(column); - return new SmiExtendedMetaData( - dbType, - maxLength, - precision, - scale, - locale.LCID, - SmiMetaData.DefaultNVarChar.CompareOptions, - column.DataType, - false, // no support for multi-valued columns in a TVP yet - null, // no support for structured columns yet - null, // no support for structured columns yet - column.ColumnName, - null, - null, - null); - } - - // Map SmiMetaData from a schema table. - // DEVNOTE: since we're using SchemaTable, we can assume that we aren't directly using a SqlDataReader - // so we don't support the Sql-specific stuff, like collation - static internal SmiExtendedMetaData SmiMetaDataFromSchemaTableRow(DataRow schemaRow) - { - // One way or another, we'll need column name, so put it in a local now to shorten code later. - string colName = ""; - object temp = schemaRow[SchemaTableColumn.ColumnName]; - if (DBNull.Value != temp) - { - colName = (string)temp; - } - - // Determine correct SqlDbType. - temp = schemaRow[SchemaTableColumn.DataType]; - if (DBNull.Value == temp) - { - throw SQL.NullSchemaTableDataTypeNotSupported(colName); - } - Type colType = (Type)temp; - SqlDbType colDbType = InferSqlDbTypeFromType_2008(colType); - if (InvalidSqlDbType == colDbType) - { - // Unknown through standard mapping, use VarBinary for columns that are Object typed, otherwise error - if (typeof(object) == colType) - { - colDbType = SqlDbType.VarBinary; - } - else - { - throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString()); - } - } - - // Determine metadata modifier values per type (maxlength, precision, scale, etc) - long maxLength = 0; - byte precision = 0; - byte scale = 0; - switch (colDbType) - { - case SqlDbType.BigInt: - case SqlDbType.Bit: - case SqlDbType.DateTime: - case SqlDbType.Float: - case SqlDbType.Image: - case SqlDbType.Int: - case SqlDbType.Money: - case SqlDbType.NText: - case SqlDbType.Real: - case SqlDbType.UniqueIdentifier: - case SqlDbType.SmallDateTime: - case SqlDbType.SmallInt: - case SqlDbType.SmallMoney: - case SqlDbType.Text: - case SqlDbType.Timestamp: - case SqlDbType.TinyInt: - case SqlDbType.Variant: - case SqlDbType.Xml: - case SqlDbType.Date: - // These types require no metadata modifies - break; - case SqlDbType.Binary: - case SqlDbType.VarBinary: - // These types need a binary max length - temp = schemaRow[SchemaTableColumn.ColumnSize]; - if (DBNull.Value == temp) - { - // source isn't specifying a size, so assume the worst - if (SqlDbType.Binary == colDbType) - { - maxLength = SmiMetaData.MaxBinaryLength; - } - else - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - } - else - { - // We (should) have a valid maxlength, so use it. - maxLength = Convert.ToInt64(temp, null); - - // Max length must be 0 to MaxBinaryLength or it can be UnlimitedMAX if type is varbinary - // If it's greater than MaxBinaryLength, just promote it to UnlimitedMAX, if possible - if (maxLength > SmiMetaData.MaxBinaryLength) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - - if ((maxLength < 0 && - (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator || - SqlDbType.Binary == colDbType))) - { - throw SQL.InvalidColumnMaxLength(colName, maxLength); - } - } - break; - case SqlDbType.Char: - case SqlDbType.VarChar: - // These types need an ANSI max length - temp = schemaRow[SchemaTableColumn.ColumnSize]; - if (DBNull.Value == temp) - { - // source isn't specifying a size, so assume the worst - if (SqlDbType.Char == colDbType) - { - maxLength = SmiMetaData.MaxANSICharacters; - } - else - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - } - else - { - // We (should) have a valid maxlength, so use it. - maxLength = Convert.ToInt64(temp, null); - - // Max length must be 0 to MaxANSICharacters or it can be UnlimitedMAX if type is varbinary - // If it's greater than MaxANSICharacters, just promote it to UnlimitedMAX, if possible - if (maxLength > SmiMetaData.MaxANSICharacters) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - - if ((maxLength < 0 && - (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator || - SqlDbType.Char == colDbType))) - { - throw SQL.InvalidColumnMaxLength(colName, maxLength); - } - } - break; - case SqlDbType.NChar: - case SqlDbType.NVarChar: - // These types need a unicode max length - temp = schemaRow[SchemaTableColumn.ColumnSize]; - if (DBNull.Value == temp) - { - // source isn't specifying a size, so assume the worst - if (SqlDbType.NChar == colDbType) - { - maxLength = SmiMetaData.MaxUnicodeCharacters; - } - else - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - } - else - { - // We (should) have a valid maxlength, so use it. - maxLength = Convert.ToInt64(temp, null); - - // Max length must be 0 to MaxUnicodeCharacters or it can be UnlimitedMAX if type is varbinary - // If it's greater than MaxUnicodeCharacters, just promote it to UnlimitedMAX, if possible - if (maxLength > SmiMetaData.MaxUnicodeCharacters) - { - maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; - } - - if ((maxLength < 0 && - (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator || - SqlDbType.NChar == colDbType))) - { - throw SQL.InvalidColumnMaxLength(colName, maxLength); - } - } - break; - case SqlDbType.Decimal: - // Decimal requires precision and scale - temp = schemaRow[SchemaTableColumn.NumericPrecision]; - if (DBNull.Value == temp) - { - precision = SmiMetaData.DefaultDecimal.Precision; - } - else - { - precision = Convert.ToByte(temp, null); - } - - temp = schemaRow[SchemaTableColumn.NumericScale]; - if (DBNull.Value == temp) - { - scale = SmiMetaData.DefaultDecimal.Scale; - } - else - { - scale = Convert.ToByte(temp, null); - } - - if (precision < SmiMetaData.MinPrecision || - precision > SqlDecimal.MaxPrecision || - scale < SmiMetaData.MinScale || - scale > SqlDecimal.MaxScale || - scale > precision) - { - throw SQL.InvalidColumnPrecScale(); - } - break; - case SqlDbType.Time: - case SqlDbType.DateTime2: - case SqlDbType.DateTimeOffset: - // requires scale - temp = schemaRow[SchemaTableColumn.NumericScale]; - if (DBNull.Value == temp) - { - scale = SmiMetaData.DefaultTime.Scale; - } - else - { - scale = Convert.ToByte(temp, null); - } - - if (scale > SmiMetaData.MaxTimeScale) - { - throw SQL.InvalidColumnPrecScale(); - } - else if (scale < 0) - { - scale = SmiMetaData.DefaultTime.Scale; - } - break; - case SqlDbType.Udt: - case SqlDbType.Structured: - default: - // These types are not supported from SchemaTable - throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString()); - } - - return new SmiExtendedMetaData( - colDbType, - maxLength, - precision, - scale, - System.Globalization.CultureInfo.CurrentCulture.LCID, - SmiMetaData.GetDefaultForType(colDbType).CompareOptions, - null, // no support for UDTs from SchemaTable - false, // no support for multi-valued columns in a TVP yet - null, // no support for structured columns yet - null, // no support for structured columns yet - colName, - null, - null, - null); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/TriggerAction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/TriggerAction.cs index bf5a789125..1f1b011ee1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/TriggerAction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/TriggerAction.cs @@ -106,161 +106,161 @@ internal enum EMDEventType // WHEN ADDING, PLEASE CHECK WITH FILE-OWNER FOR WHICH NUMBERS TO USE. THANKS! }; - /// + /// public enum TriggerAction { - /// + /// Invalid = EMDEventType.x_eet_Invalid, - /// + /// Insert = EMDEventType.x_eet_Insert, - /// + /// Update = EMDEventType.x_eet_Update, - /// + /// Delete = EMDEventType.x_eet_Delete, - /// + /// CreateTable = EMDEventType.x_eet_Create_Table, - /// + /// AlterTable = EMDEventType.x_eet_Alter_Table, - /// + /// DropTable = EMDEventType.x_eet_Drop_Table, - /// + /// CreateIndex = EMDEventType.x_eet_Create_Index, - /// + /// AlterIndex = EMDEventType.x_eet_Alter_Index, - /// + /// DropIndex = EMDEventType.x_eet_Drop_Index, - /// + /// CreateSynonym = EMDEventType.x_eet_Create_Synonym, - /// + /// DropSynonym = EMDEventType.x_eet_Drop_Synonym, - /// + /// CreateSecurityExpression = EMDEventType.x_eet_Create_Secexpr, - /// + /// DropSecurityExpression = EMDEventType.x_eet_Drop_Secexpr, - /// + /// CreateView = EMDEventType.x_eet_Create_View, - /// + /// AlterView = EMDEventType.x_eet_Alter_View, - /// + /// DropView = EMDEventType.x_eet_Drop_View, - /// + /// CreateProcedure = EMDEventType.x_eet_Create_Procedure, - /// + /// AlterProcedure = EMDEventType.x_eet_Alter_Procedure, - /// + /// DropProcedure = EMDEventType.x_eet_Drop_Procedure, - /// + /// CreateFunction = EMDEventType.x_eet_Create_Function, - /// + /// AlterFunction = EMDEventType.x_eet_Alter_Function, - /// + /// DropFunction = EMDEventType.x_eet_Drop_Function, - /// + /// CreateTrigger = EMDEventType.x_eet_Create_Trigger, - /// + /// AlterTrigger = EMDEventType.x_eet_Alter_Trigger, - /// + /// DropTrigger = EMDEventType.x_eet_Drop_Trigger, - /// + /// CreateEventNotification = EMDEventType.x_eet_Create_Event_Notification, - /// + /// DropEventNotification = EMDEventType.x_eet_Drop_Event_Notification, - /// + /// CreateType = EMDEventType.x_eet_Create_Type, // Alter_Type = EMDEventType.x_eet_Alter_Type, - /// + /// DropType = EMDEventType.x_eet_Drop_Type, - /// + /// CreateAssembly = EMDEventType.x_eet_Create_Assembly, - /// + /// AlterAssembly = EMDEventType.x_eet_Alter_Assembly, - /// + /// DropAssembly = EMDEventType.x_eet_Drop_Assembly, - /// + /// CreateUser = EMDEventType.x_eet_Create_User, - /// + /// AlterUser = EMDEventType.x_eet_Alter_User, - /// + /// DropUser = EMDEventType.x_eet_Drop_User, - /// + /// CreateRole = EMDEventType.x_eet_Create_Role, - /// + /// AlterRole = EMDEventType.x_eet_Alter_Role, - /// + /// DropRole = EMDEventType.x_eet_Drop_Role, - /// + /// CreateAppRole = EMDEventType.x_eet_Create_AppRole, - /// + /// AlterAppRole = EMDEventType.x_eet_Alter_AppRole, - /// + /// DropAppRole = EMDEventType.x_eet_Drop_AppRole, - /// + /// CreateSchema = EMDEventType.x_eet_Create_Schema, - /// + /// AlterSchema = EMDEventType.x_eet_Alter_Schema, - /// + /// DropSchema = EMDEventType.x_eet_Drop_Schema, - /// + /// CreateLogin = EMDEventType.x_eet_Create_Login, - /// + /// AlterLogin = EMDEventType.x_eet_Alter_Login, - /// + /// DropLogin = EMDEventType.x_eet_Drop_Login, - /// + /// CreateMsgType = EMDEventType.x_eet_Create_MsgType, - /// + /// DropMsgType = EMDEventType.x_eet_Drop_MsgType, - /// + /// CreateContract = EMDEventType.x_eet_Create_Contract, - /// + /// DropContract = EMDEventType.x_eet_Drop_Contract, - /// + /// CreateQueue = EMDEventType.x_eet_Create_Queue, - /// + /// AlterQueue = EMDEventType.x_eet_Alter_Queue, - /// + /// DropQueue = EMDEventType.x_eet_Drop_Queue, - /// + /// CreateService = EMDEventType.x_eet_Create_Service, - /// + /// AlterService = EMDEventType.x_eet_Alter_Service, - /// + /// DropService = EMDEventType.x_eet_Drop_Service, - /// + /// CreateRoute = EMDEventType.x_eet_Create_Route, - /// + /// AlterRoute = EMDEventType.x_eet_Alter_Route, - /// + /// DropRoute = EMDEventType.x_eet_Drop_Route, - /// + /// GrantStatement = EMDEventType.x_eet_Grant_Statement, - /// + /// DenyStatement = EMDEventType.x_eet_Deny_Statement, - /// + /// RevokeStatement = EMDEventType.x_eet_Revoke_Statement, - /// + /// GrantObject = EMDEventType.x_eet_Grant_Object, - /// + /// DenyObject = EMDEventType.x_eet_Deny_Object, - /// + /// RevokeObject = EMDEventType.x_eet_Revoke_Object, - /// + /// CreateBinding = EMDEventType.x_eet_Create_Binding, - /// + /// AlterBinding = EMDEventType.x_eet_Alter_Binding, - /// + /// DropBinding = EMDEventType.x_eet_Drop_Binding, - /// + /// CreatePartitionFunction = EMDEventType.x_eet_Create_Partition_Function, - /// + /// AlterPartitionFunction = EMDEventType.x_eet_Alter_Partition_Function, - /// + /// DropPartitionFunction = EMDEventType.x_eet_Drop_Partition_Function, - /// + /// CreatePartitionScheme = EMDEventType.x_eet_Create_Partition_Scheme, - /// + /// AlterPartitionScheme = EMDEventType.x_eet_Alter_Partition_Scheme, - /// + /// DropPartitionScheme = EMDEventType.x_eet_Drop_Partition_Scheme, } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlser.cs deleted file mode 100644 index 2967aaaa55..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/sqlser.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using System.IO; -using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient.Server -{ - internal class SerializationHelperSql9 - { - // Don't let anyone create an instance of this class. - private SerializationHelperSql9() { } - - // Get the m_size of the serialized stream for this type, in bytes. - // This method creates an instance of the type using the public - // no-argument constructor, serializes it, and returns the m_size - // in bytes. - // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. - [MethodImpl(MethodImplOptions.NoInlining)] - internal static int SizeInBytes(Type t) => SizeInBytes(Activator.CreateInstance(t)); - - // Get the m_size of the serialized stream for this type, in bytes. - internal static int SizeInBytes(object instance) - { - Type t = instance.GetType(); - Format k = GetFormat(t); - DummyStream stream = new DummyStream(); - Serializer ser = GetSerializer(instance.GetType()); - ser.Serialize(stream, instance); - return (int)stream.Length; - } - - internal static void Serialize(Stream s, object instance) - { - GetSerializer(instance.GetType()).Serialize(s, instance); - } - - internal static object Deserialize(Stream s, Type resultType) => GetSerializer(resultType).Deserialize(s); - - private static Format GetFormat(Type t) => GetUdtAttribute(t).Format; - - // Cache the relationship between a type and its serializer. - // This is expensive to compute since it involves traversing the - // custom attributes of the type using reflection. - // - // Use a per-thread cache, so that there are no synchronization - // issues when accessing cache entries from multiple threads. - [ThreadStatic] - private static Hashtable s_types2Serializers; - - private static Serializer GetSerializer(Type t) - { - if (s_types2Serializers == null) - s_types2Serializers = new Hashtable(); - - Serializer s = (Serializer)s_types2Serializers[t]; - if (s == null) - { - s = GetNewSerializer(t); - s_types2Serializers[t] = s; - } - return s; - } - - internal static int GetUdtMaxLength(Type t) - { - SqlUdtInfo udtInfo = SqlUdtInfo.GetFromType(t); - - if (Format.Native == udtInfo.SerializationFormat) - { - // In the native format, the user does not specify the - // max byte size, it is computed from the type definition - return SerializationHelperSql9.SizeInBytes(t); - } - else - { - // In all other formats, the user specifies the maximum size in bytes. - return udtInfo.MaxByteSize; - } - } - - private static object[] GetCustomAttributes(Type t) - { - object[] attrs = t.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); - - // If we don't find a Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute, - // search for a Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute from the - // old System.Data.SqlClient assembly and copy it to our - // Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute for reference. - if (attrs == null || attrs.Length == 0) - { - object[] attr = t.GetCustomAttributes(false); - attrs = new object[0]; - if (attr != null && attr.Length > 0) - { - for (int i = 0; i < attr.Length; i++) - { - if (attr[i].GetType().FullName.Equals("Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute")) - { - SqlUserDefinedTypeAttribute newAttr = null; - PropertyInfo[] sourceProps = attr[i].GetType().GetProperties(); - - foreach (PropertyInfo sourceProp in sourceProps) - { - if (sourceProp.Name.Equals("Format")) - { - newAttr = new SqlUserDefinedTypeAttribute((Format)sourceProp.GetValue(attr[i], null)); - break; - } - } - if (newAttr != null) - { - foreach (PropertyInfo targetProp in newAttr.GetType().GetProperties()) - { - if (targetProp.CanRead && targetProp.CanWrite) - { - object copyValue = attr[i].GetType().GetProperty(targetProp.Name).GetValue(attr[i]); - targetProp.SetValue(newAttr, copyValue); - } - } - } - - attrs = new object[1] { newAttr }; - break; - } - } - } - } - - return attrs; - } - - internal static SqlUserDefinedTypeAttribute GetUdtAttribute(Type t) - { - SqlUserDefinedTypeAttribute udtAttr = null; - object[] attr = GetCustomAttributes(t); - - if (attr != null && attr.Length == 1) - { - udtAttr = (SqlUserDefinedTypeAttribute)attr[0]; - } - else - { - Type InvalidUdtExceptionType = typeof(InvalidUdtException); - var arguments = new Type[] { typeof(Type), typeof(String) }; - MethodInfo Create = InvalidUdtExceptionType.GetMethod("Create", arguments); - Create.Invoke(null, new object[] { t, Strings.SqlUdtReason_NoUdtAttribute }); - } - return udtAttr; - } - - // Create a new serializer for the given type. - private static Serializer GetNewSerializer(Type t) - { - SqlUserDefinedTypeAttribute udtAttr = GetUdtAttribute(t); - - switch (udtAttr.Format) - { - case Format.Native: - return new NormalizedSerializer(t); - case Format.UserDefined: - return new BinarySerializeSerializer(t); - case Format.Unknown: // should never happen, but fall through - default: - throw ADP.InvalidUserDefinedTypeSerializationFormat(udtAttr.Format); - } - } - } - - // The base serializer class. - internal abstract class Serializer - { - public abstract object Deserialize(Stream s); - public abstract void Serialize(Stream s, object o); - protected Type _type; - - protected Serializer(Type t) - { - _type = t; - } - } - - internal sealed class NormalizedSerializer : Serializer - { - private BinaryOrderedUdtNormalizer _normalizer; - private bool _isFixedSize; - private int _maxSize; - - internal NormalizedSerializer(Type t) : base(t) - { - SqlUserDefinedTypeAttribute udtAttr = SerializationHelperSql9.GetUdtAttribute(t); - _normalizer = new BinaryOrderedUdtNormalizer(t, true); - _isFixedSize = udtAttr.IsFixedLength; - _maxSize = _normalizer.Size; - } - - public override void Serialize(Stream s, object o) => _normalizer.NormalizeTopObject(o, s); - - public override object Deserialize(Stream s) => _normalizer.DeNormalizeTopObject(_type, s); - } - - internal sealed class BinarySerializeSerializer : Serializer - { - internal BinarySerializeSerializer(Type t) : base(t) - { - } - - public override void Serialize(Stream s, object o) - { - BinaryWriter w = new BinaryWriter(s); - if (o is Microsoft.SqlServer.Server.IBinarySerialize) - { - ((SqlServer.Server.IBinarySerialize)o).Write(w); - } - else - { - ((IBinarySerialize)o).Write(w); - } - } - - // Prevent inlining so that reflection calls are not moved - // to a caller that may be in a different assembly that may - // have a different grant set. - [MethodImpl(MethodImplOptions.NoInlining)] - public override object Deserialize(Stream s) - { - object instance = Activator.CreateInstance(_type); - BinaryReader r = new BinaryReader(s); - if (instance is Microsoft.SqlServer.Server.IBinarySerialize) - { - ((SqlServer.Server.IBinarySerialize)instance).Read(r); - } - else - { - ((IBinarySerialize)instance).Read(r); - } - return instance; - } - } - - // A dummy stream class, used to get the number of bytes written - // to the stream. - internal sealed class DummyStream : Stream - { - private long _size; - - public DummyStream() - { - } - - private void DontDoIt() - { - throw new Exception(StringsHelper.GetString(Strings.Sql_InternalError)); - } - - public override bool CanRead => false; - - public override bool CanWrite => true; - - public override bool CanSeek => false; - - public override long Position - { - get => _size; - set =>_size = value; - } - - public override long Length => _size; - - public override void SetLength(long value) => _size = value; - - public override long Seek(long value, SeekOrigin loc) - { - DontDoIt(); - return -1; - } - - public override void Flush() - { - } - - public override int Read(byte[] buffer, int offset, int count) - { - DontDoIt(); - return -1; - } - - public override void Write(byte[] buffer, int offset, int count) => _size += count; - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs deleted file mode 100644 index f2d332c95f..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ /dev/null @@ -1,1400 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; -using Microsoft.Data.SqlTypes; - -namespace Microsoft.Data.SqlClient -{ - internal sealed partial class SqlBuffer - { - internal enum StorageType - { - Empty = 0, - Boolean, - Byte, - DateTime, - Decimal, - Double, - Int16, - Int32, - Int64, - Guid, - Money, - Single, - String, - SqlBinary, - SqlCachedBuffer, - SqlGuid, - SqlXml, - Date, - DateTime2, - DateTimeOffset, - Time, - } - - internal struct DateTimeInfo - { - // This is used to store DateTime - internal int _daypart; - internal int _timepart; - } - - internal struct NumericInfo - { - // This is used to store Decimal data - internal int _data1; - internal int _data2; - internal int _data3; - internal int _data4; - internal byte _precision; - internal byte _scale; - internal bool _positive; - } - - internal struct TimeInfo - { - internal long _ticks; - internal byte _scale; - } - - internal struct DateTime2Info - { - internal int _date; - internal TimeInfo _timeInfo; - } - - internal struct DateTimeOffsetInfo - { - internal DateTime2Info _dateTime2Info; - internal short _offset; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct Storage - { - [FieldOffset(0)] - internal bool _boolean; - [FieldOffset(0)] - internal byte _byte; - [FieldOffset(0)] - internal DateTimeInfo _dateTimeInfo; - [FieldOffset(0)] - internal double _double; - [FieldOffset(0)] - internal NumericInfo _numericInfo; - [FieldOffset(0)] - internal short _int16; - [FieldOffset(0)] - internal int _int32; - [FieldOffset(0)] - internal long _int64; // also used to store Money, UtcDateTime, Date , and Time - [FieldOffset(0)] - internal Guid _guid; - [FieldOffset(0)] - internal float _single; - [FieldOffset(0)] - internal TimeInfo _timeInfo; - [FieldOffset(0)] - internal DateTime2Info _dateTime2Info; - [FieldOffset(0)] - internal DateTimeOffsetInfo _dateTimeOffsetInfo; - } - - private bool _isNull; - private StorageType _type; - private Storage _value; - private object _object; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml - - internal SqlBuffer() - { - } - - private SqlBuffer(SqlBuffer value) - { // Clone - // value types - _isNull = value._isNull; - _type = value._type; - // ref types - should also be read only unless at some point we allow this data - // to be mutable, then we will need to copy - _value = value._value; - _object = value._object; - } - - internal bool IsEmpty => _type == StorageType.Empty; - - internal bool IsNull => _isNull; - - internal StorageType VariantInternalStorageType => _type; - - internal bool Boolean - { - get - { - ThrowIfNull(); - - if (StorageType.Boolean == _type) - { - return _value._boolean; - } - return (bool)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._boolean = value; - _type = StorageType.Boolean; - _isNull = false; - } - } - - internal byte Byte - { - get - { - ThrowIfNull(); - - if (StorageType.Byte == _type) - { - return _value._byte; - } - return (byte)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._byte = value; - _type = StorageType.Byte; - _isNull = false; - } - } - - internal byte[] ByteArray - { - get - { - ThrowIfNull(); - return SqlBinary.Value; - } - } - - internal DateTime DateTime - { - get - { - ThrowIfNull(); - - if (StorageType.Date == _type) - { - return DateTime.MinValue.AddDays(_value._int32); - } - if (StorageType.DateTime2 == _type) - { - return new DateTime(GetTicksFromDateTime2Info(_value._dateTime2Info)); - } - if (StorageType.DateTime == _type) - { - return SqlTypeWorkarounds.SqlDateTimeToDateTime(_value._dateTimeInfo._daypart, _value._dateTimeInfo._timepart); - } - return (DateTime)Value; // anything else we haven't thought of goes through boxing. - } - } - - #region Decimal - internal decimal Decimal - { - get - { - ThrowIfNull(); - - if (StorageType.Decimal == _type) - { - if (_value._numericInfo._data4 != 0 || _value._numericInfo._scale > 28) - { - // Only removing trailing zeros from a decimal part won't hit its value! - if (_value._numericInfo._scale > 0) - { - int zeroCnt = FindTrailingZerosAndPrec((uint)_value._numericInfo._data1, (uint)_value._numericInfo._data2, - (uint)_value._numericInfo._data3, (uint)_value._numericInfo._data4, - _value._numericInfo._scale, out int precision); - - int minScale = _value._numericInfo._scale - zeroCnt; // minimum possible sacle after removing the trailing zeros. - - if (zeroCnt > 0 && minScale <= 28 && precision <= 29) - { - SqlDecimal sqlValue = new(_value._numericInfo._precision, _value._numericInfo._scale, _value._numericInfo._positive, - _value._numericInfo._data1, _value._numericInfo._data2, - _value._numericInfo._data3, _value._numericInfo._data4); - - int integral = precision - minScale; - int newPrec = 29; - - if (integral != 1 && precision != 29) - { - newPrec = 28; - } - - try - { - // Precision could be 28 or 29 - // ex: (precision == 29 && scale == 28) - // valid: (+/-)7.1234567890123456789012345678 - // invalid: (+/-)8.1234567890123456789012345678 - return SqlDecimal.ConvertToPrecScale(sqlValue, newPrec, newPrec - integral).Value; - } - catch (OverflowException) - { - throw new OverflowException(SQLResource.ConversionOverflowMessage); - } - } - } - throw new OverflowException(SQLResource.ConversionOverflowMessage); - } - return new decimal(_value._numericInfo._data1, _value._numericInfo._data2, _value._numericInfo._data3, !_value._numericInfo._positive, _value._numericInfo._scale); - } - if (StorageType.Money == _type) - { - long l = _value._int64; - bool isNegative = false; - if (l < 0) - { - isNegative = true; - l = -l; - } - return new decimal((int)(l & 0xffffffff), (int)(l >> 32), 0, isNegative, 4); - } - return (decimal)Value; // anything else we haven't thought of goes through boxing. - } - } - - /// - /// Returns number of trailing zeros using the supplied parameters. - /// - /// An 32-bit unsigned integer which will be combined with data2, data3, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data3, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data2, and data4 - /// An 32-bit unsigned integer which will be combined with data1, data2, and data3 - /// The number of decimal places - /// OUT |The number of digits without trailing zeros - /// Number of trailing zeros - private static int FindTrailingZerosAndPrec(uint data1, uint data2, uint data3, uint data4, byte scale, out int valuablePrecision) - { - // Make local copy of data to avoid modifying input. - Span rgulNumeric = stackalloc uint[4] { data1, data2, data3, data4 }; - int zeroCnt = 0; //Number of trailing zero digits - int precCnt = 0; //Valuable precision - uint uiRem = 0; //Remainder of a division by 10 - int len = 4; // Max possible items - - //Retrieve each digit from the lowest significant digit - while (len > 1 || rgulNumeric[0] != 0) - { - SqlDecimalDivBy(rgulNumeric, ref len, 10, out uiRem); - if (uiRem == 0 && precCnt == 0) - { - zeroCnt++; - } - else - { - precCnt++; - } - } - - if (uiRem == 0) - { - zeroCnt = scale; - } - - // if scale of the number has not been reached, pad remaining number with zeros. - if (zeroCnt + precCnt <= scale) - { - precCnt = scale - zeroCnt + 1; - } - valuablePrecision = precCnt; - return zeroCnt; - } - - /// - /// Multi-precision one super-digit divide in place. - /// U = U / D, - /// R = U % D - /// (Length of U can decrease) - /// - /// InOut | U - /// InOut | Number of items with non-zero value in U between 1 to 4 - /// In | D - /// Out | R - private static void SqlDecimalDivBy(Span data, ref int len, uint divisor, out uint remainder) - { - uint uiCarry = 0; - ulong ulAccum; - ulong ulDivisor = (ulong)divisor; - int iLen = len; - - while (iLen > 0) - { - iLen--; - ulAccum = (((ulong)uiCarry) << 32) + (ulong)(data[iLen]); - data[iLen] = (uint)(ulAccum / ulDivisor); - uiCarry = (uint)(ulAccum - (ulong)data[iLen] * ulDivisor); // (ULONG) (ulAccum % divisor) - } - remainder = uiCarry; - - // Normalize multi-precision number - remove leading zeroes - while (len > 1 && data[len - 1] == 0) - { len--; } - } - #endregion - - internal double Double - { - get - { - ThrowIfNull(); - - if (StorageType.Double == _type) - { - return _value._double; - } - return (double)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._double = value; - _type = StorageType.Double; - _isNull = false; - } - } - - internal Guid Guid - { - get - { - ThrowIfNull(); - if (StorageType.Guid == _type) - { - return _value._guid; - } - else if (StorageType.SqlGuid == _type) - { - return ((SqlGuid)_object).Value; - } - return (Guid)Value; - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _type = StorageType.Guid; - _value._guid = value; - _isNull = false; - } - } - - internal short Int16 - { - get - { - ThrowIfNull(); - - if (StorageType.Int16 == _type) - { - return _value._int16; - } - return (short)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int16 = value; - _type = StorageType.Int16; - _isNull = false; - } - } - - internal int Int32 - { - get - { - ThrowIfNull(); - - if (StorageType.Int32 == _type) - { - return _value._int32; - } - return (int)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int32 = value; - _type = StorageType.Int32; - _isNull = false; - } - } - - internal long Int64 - { - get - { - ThrowIfNull(); - - if (StorageType.Int64 == _type) - { - return _value._int64; - } - return (long)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int64 = value; - _type = StorageType.Int64; - _isNull = false; - } - } - - internal float Single - { - get - { - ThrowIfNull(); - - if (StorageType.Single == _type) - { - return _value._single; - } - return (float)Value; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._single = value; - _type = StorageType.Single; - _isNull = false; - } - } - - internal string String - { - get - { - ThrowIfNull(); - - if (StorageType.String == _type) - { - return (string)_object; - } - else if (StorageType.SqlCachedBuffer == _type) - { - return ((SqlCachedBuffer)(_object)).ToString(); - } - return (string)Value; // anything else we haven't thought of goes through boxing. - } - } - - // use static list of format strings indexed by scale for perf - private static readonly string[] s_sql2008DateTimeOffsetFormatByScale = new string[] { - "yyyy-MM-dd HH:mm:ss zzz", - "yyyy-MM-dd HH:mm:ss.f zzz", - "yyyy-MM-dd HH:mm:ss.ff zzz", - "yyyy-MM-dd HH:mm:ss.fff zzz", - "yyyy-MM-dd HH:mm:ss.ffff zzz", - "yyyy-MM-dd HH:mm:ss.fffff zzz", - "yyyy-MM-dd HH:mm:ss.ffffff zzz", - "yyyy-MM-dd HH:mm:ss.fffffff zzz", - }; - - private static readonly string[] s_sql2008DateTime2FormatByScale = new string[] { - "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-dd HH:mm:ss.f", - "yyyy-MM-dd HH:mm:ss.ff", - "yyyy-MM-dd HH:mm:ss.fff", - "yyyy-MM-dd HH:mm:ss.ffff", - "yyyy-MM-dd HH:mm:ss.fffff", - "yyyy-MM-dd HH:mm:ss.ffffff", - "yyyy-MM-dd HH:mm:ss.fffffff", - }; - - private static readonly string[] s_sql2008TimeFormatByScale = new string[] { - "HH:mm:ss", - "HH:mm:ss.f", - "HH:mm:ss.ff", - "HH:mm:ss.fff", - "HH:mm:ss.ffff", - "HH:mm:ss.fffff", - "HH:mm:ss.ffffff", - "HH:mm:ss.fffffff", - }; - - internal string Sql2008DateTimeString - { - get - { - ThrowIfNull(); - - if (StorageType.Date == _type) - { - return DateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.Time == _type) - { - byte scale = _value._timeInfo._scale; - return new DateTime(_value._timeInfo._ticks).ToString(s_sql2008TimeFormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.DateTime2 == _type) - { - byte scale = _value._dateTime2Info._timeInfo._scale; - return DateTime.ToString(s_sql2008DateTime2FormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - if (StorageType.DateTimeOffset == _type) - { - DateTimeOffset dto = DateTimeOffset; - byte scale = _value._dateTimeOffsetInfo._dateTime2Info._timeInfo._scale; - return dto.ToString(s_sql2008DateTimeOffsetFormatByScale[scale], DateTimeFormatInfo.InvariantInfo); - } - return (string)Value; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlString Sql2008DateTimeSqlString - { - get - { - if (StorageType.Date == _type || - StorageType.Time == _type || - StorageType.DateTime2 == _type || - StorageType.DateTimeOffset == _type) - { - if (IsNull) - { - return SqlString.Null; - } - return new SqlString(Sql2008DateTimeString); - } - return (SqlString)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal TimeSpan Time - { - get - { - ThrowIfNull(); - - if (StorageType.Time == _type) - { - return new TimeSpan(_value._timeInfo._ticks); - } - - return (TimeSpan)Value; // anything else we haven't thought of goes through boxing. - } - } - - internal DateTimeOffset DateTimeOffset - { - get - { - ThrowIfNull(); - - if (StorageType.DateTimeOffset == _type) - { - TimeSpan offset = new TimeSpan(0, _value._dateTimeOffsetInfo._offset, 0); - // datetime part presents time in UTC - return new DateTimeOffset(GetTicksFromDateTime2Info(_value._dateTimeOffsetInfo._dateTime2Info) + offset.Ticks, offset); - } - - return (DateTimeOffset)Value; // anything else we haven't thought of goes through boxing. - } - } - - private static long GetTicksFromDateTime2Info(DateTime2Info dateTime2Info) - { - return (dateTime2Info._date * TimeSpan.TicksPerDay + dateTime2Info._timeInfo._ticks); - } - - internal SqlBinary SqlBinary - { - get - { - if (StorageType.SqlBinary == _type) - { - return (SqlBinary)_object; - } - return (SqlBinary)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlBinary; - _isNull = value.IsNull; - } - } - - internal SqlBoolean SqlBoolean - { - get - { - if (StorageType.Boolean == _type) - { - if (IsNull) - { - return SqlBoolean.Null; - } - return new SqlBoolean(_value._boolean); - } - return (SqlBoolean)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlByte SqlByte - { - get - { - if (StorageType.Byte == _type) - { - if (IsNull) - { - return SqlByte.Null; - } - return new SqlByte(_value._byte); - } - return (SqlByte)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlCachedBuffer SqlCachedBuffer - { - get - { - if (StorageType.SqlCachedBuffer == _type) - { - if (IsNull) - { - return SqlCachedBuffer.Null; - } - return (SqlCachedBuffer)_object; - } - return (SqlCachedBuffer)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlCachedBuffer; - _isNull = value.IsNull; - } - } - - internal SqlXml SqlXml - { - get - { - if (StorageType.SqlXml == _type) - { - if (IsNull) - { - return SqlXml.Null; - } - return (SqlXml)_object; - } - return (SqlXml)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlXml; - _isNull = value.IsNull; - } - } - - internal SqlDateTime SqlDateTime - { - get - { - if (StorageType.DateTime == _type) - { - if (IsNull) - { - return SqlDateTime.Null; - } - return new SqlDateTime(_value._dateTimeInfo._daypart, _value._dateTimeInfo._timepart); - } - return (SqlDateTime)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlDecimal SqlDecimal - { - get - { - if (StorageType.Decimal == _type) - { - if (IsNull) - { - return SqlDecimal.Null; - } - return new SqlDecimal(_value._numericInfo._precision, - _value._numericInfo._scale, - _value._numericInfo._positive, - _value._numericInfo._data1, - _value._numericInfo._data2, - _value._numericInfo._data3, - _value._numericInfo._data4 - ); - } - return (SqlDecimal)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlDouble SqlDouble - { - get - { - if (StorageType.Double == _type) - { - if (IsNull) - { - return SqlDouble.Null; - } - return new SqlDouble(_value._double); - } - return (SqlDouble)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlGuid SqlGuid - { - get - { - if (StorageType.Guid == _type) - { - return new SqlGuid(_value._guid); - } - else if (StorageType.SqlGuid == _type) - { - return IsNull ? SqlGuid.Null : (SqlGuid)_object; - } - return (SqlGuid)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlGuid; - _isNull = value.IsNull; - } - } - - internal SqlInt16 SqlInt16 - { - get - { - if (StorageType.Int16 == _type) - { - if (IsNull) - { - return SqlInt16.Null; - } - return new SqlInt16(_value._int16); - } - return (SqlInt16)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlInt32 SqlInt32 - { - get - { - if (StorageType.Int32 == _type) - { - if (IsNull) - { - return SqlInt32.Null; - } - return new SqlInt32(_value._int32); - } - return (SqlInt32)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlInt64 SqlInt64 - { - get - { - if (StorageType.Int64 == _type) - { - if (IsNull) - { - return SqlInt64.Null; - } - return new SqlInt64(_value._int64); - } - return (SqlInt64)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlMoney SqlMoney - { - get - { - if (StorageType.Money == _type) - { - if (IsNull) - { - return SqlMoney.Null; - } - return SqlTypeWorkarounds.SqlMoneyCtor(_value._int64, 1/*ignored*/); - } - return (SqlMoney)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlSingle SqlSingle - { - get - { - if (StorageType.Single == _type) - { - if (IsNull) - { - return SqlSingle.Null; - } - return new SqlSingle(_value._single); - } - return (SqlSingle)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal SqlString SqlString - { - get - { - if (StorageType.String == _type) - { - if (IsNull) - { - return SqlString.Null; - } - return new SqlString((string)_object); - } - else if (StorageType.SqlCachedBuffer == _type) - { - SqlCachedBuffer data = (SqlCachedBuffer)(_object); - if (data.IsNull) - { - return SqlString.Null; - } - return data.ToSqlString(); - } - return (SqlString)SqlValue; // anything else we haven't thought of goes through boxing. - } - } - - internal object SqlValue - { - get - { - switch (_type) - { - case StorageType.Empty: - return DBNull.Value; - case StorageType.Boolean: - return SqlBoolean; - case StorageType.Byte: - return SqlByte; - case StorageType.DateTime: - return SqlDateTime; - case StorageType.Decimal: - return SqlDecimal; - case StorageType.Double: - return SqlDouble; - case StorageType.Int16: - return SqlInt16; - case StorageType.Int32: - return SqlInt32; - case StorageType.Int64: - return SqlInt64; - case StorageType.Guid: - return SqlGuid; - case StorageType.Money: - return SqlMoney; - case StorageType.Single: - return SqlSingle; - case StorageType.String: - return SqlString; - - case StorageType.SqlCachedBuffer: - { - SqlCachedBuffer data = (SqlCachedBuffer)(_object); - if (data.IsNull) - { - return SqlXml.Null; - } - return data.ToSqlXml(); - } - - case StorageType.SqlBinary: - case StorageType.SqlGuid: - return _object; - - case StorageType.SqlXml: - if (_isNull) - { - return SqlXml.Null; - } - Debug.Assert(null != _object); - return (SqlXml)_object; - - case StorageType.Date: - case StorageType.DateTime2: - if (_isNull) - { - return DBNull.Value; - } - return DateTime; - - case StorageType.DateTimeOffset: - if (_isNull) - { - return DBNull.Value; - } - return DateTimeOffset; - - case StorageType.Time: - if (_isNull) - { - return DBNull.Value; - } - return Time; - } - return null; // need to return the value as an object of some SQL type - } - } - - - // these variables store pre-boxed bool values to be used when returning a boolean - // in a object typed location, if these are not used a new value is boxed each time - // one is needed which leads to a lot of garbage which needs to be collected - private static readonly object s_cachedTrueObject = true; - private static readonly object s_cachedFalseObject = false; - - internal object Value - { - get - { - if (IsNull) - { - return DBNull.Value; - } - switch (_type) - { - case StorageType.Empty: - return DBNull.Value; - case StorageType.Boolean: - return Boolean ? s_cachedTrueObject : s_cachedFalseObject; // return pre-boxed values for perf - case StorageType.Byte: - return Byte; - case StorageType.DateTime: - return DateTime; - case StorageType.Decimal: - return Decimal; - case StorageType.Double: - return Double; - case StorageType.Int16: - return Int16; - case StorageType.Int32: - return Int32; - case StorageType.Int64: - return Int64; - case StorageType.Guid: - return Guid; - case StorageType.Money: - return Decimal; - case StorageType.Single: - return Single; - case StorageType.String: - return String; - case StorageType.SqlBinary: - return ByteArray; - case StorageType.SqlCachedBuffer: - { - // If we have a CachedBuffer, it's because it's an XMLTYPE column - // and we have to return a string when they're asking for the CLS - // value of the column. - return ((SqlCachedBuffer)(_object)).ToString(); - } - case StorageType.SqlGuid: - return Guid; - case StorageType.SqlXml: - { - // XMLTYPE columns must be returned as string when asking for the CLS value - SqlXml data = (SqlXml)_object; - string s = data.Value; - return s; - } - case StorageType.Date: - return DateTime; - case StorageType.DateTime2: - return DateTime; - case StorageType.DateTimeOffset: - return DateTimeOffset; - case StorageType.Time: - return Time; - } - return null; // need to return the value as an object of some CLS type - } - } - - internal Type GetTypeFromStorageType(bool isSqlType) - { - if (isSqlType) - { - switch (_type) - { - case StorageType.Empty: - return null; - case StorageType.Boolean: - return typeof(SqlBoolean); - case StorageType.Byte: - return typeof(SqlByte); - case StorageType.DateTime: - return typeof(SqlDateTime); - case StorageType.Decimal: - return typeof(SqlDecimal); - case StorageType.Double: - return typeof(SqlDouble); - case StorageType.Int16: - return typeof(SqlInt16); - case StorageType.Int32: - return typeof(SqlInt32); - case StorageType.Int64: - return typeof(SqlInt64); - case StorageType.Guid: - return typeof(SqlGuid); - case StorageType.Money: - return typeof(SqlMoney); - case StorageType.Single: - return typeof(SqlSingle); - case StorageType.String: - return typeof(SqlString); - case StorageType.SqlCachedBuffer: - return typeof(SqlString); - case StorageType.SqlBinary: - return typeof(object); - case StorageType.SqlGuid: - return typeof(SqlGuid); - case StorageType.SqlXml: - return typeof(SqlXml); - // Date DateTime2 and DateTimeOffset have no direct Sql type to contain them - } - } - else - { //Is CLR Type - switch (_type) - { - case StorageType.Empty: - return null; - case StorageType.Boolean: - return typeof(bool); - case StorageType.Byte: - return typeof(byte); - case StorageType.DateTime: - return typeof(DateTime); - case StorageType.Decimal: - return typeof(decimal); - case StorageType.Double: - return typeof(double); - case StorageType.Int16: - return typeof(short); - case StorageType.Int32: - return typeof(int); - case StorageType.Int64: - return typeof(long); - case StorageType.Guid: - return typeof(Guid); - case StorageType.Money: - return typeof(decimal); - case StorageType.Single: - return typeof(float); - case StorageType.String: - return typeof(string); - case StorageType.SqlBinary: - return typeof(byte[]); - case StorageType.SqlCachedBuffer: - return typeof(string); - case StorageType.SqlGuid: - return typeof(Guid); - case StorageType.SqlXml: - return typeof(string); - } - } - - return null; // need to return the value as an object of some CLS type - } - - internal static SqlBuffer[] CreateBufferArray(int length) - { - SqlBuffer[] buffers = new SqlBuffer[length]; - for (int i = 0; i < buffers.Length; ++i) - { - buffers[i] = new SqlBuffer(); - } - return buffers; - } - - internal static SqlBuffer[] CloneBufferArray(SqlBuffer[] values) - { - SqlBuffer[] copy = new SqlBuffer[values.Length]; - for (int i = 0; i < values.Length; i++) - { - copy[i] = new SqlBuffer(values[i]); - } - return copy; - } - - internal static void Clear(SqlBuffer[] values) - { - if (null != values) - { - for (int i = 0; i < values.Length; ++i) - { - values[i].Clear(); - } - } - } - - internal void Clear() - { - _isNull = false; - _type = StorageType.Empty; - _object = null; - } - - internal void SetToDateTime(int daypart, int timepart) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._dateTimeInfo._daypart = daypart; - _value._dateTimeInfo._timepart = timepart; - _type = StorageType.DateTime; - _isNull = false; - } - - internal void SetToDecimal(byte precision, byte scale, bool positive, int[] bits) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._numericInfo._precision = precision; - _value._numericInfo._scale = scale; - _value._numericInfo._positive = positive; - _value._numericInfo._data1 = bits[0]; - _value._numericInfo._data2 = bits[1]; - _value._numericInfo._data3 = bits[2]; - _value._numericInfo._data4 = bits[3]; - _type = StorageType.Decimal; - _isNull = false; - } - - internal void SetToMoney(long value) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _value._int64 = value; - _type = StorageType.Money; - _isNull = false; - } - - internal void SetToNullOfType(StorageType storageType) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _type = storageType; - _isNull = true; - _object = null; - } - - internal void SetToString(string value) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.String; - _isNull = false; - } - - internal void SetToDate(byte[] bytes) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Date; - _value._int32 = GetDateFromByteArray(bytes, 0); - _isNull = false; - } - - internal void SetToDate(DateTime date) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Date; - _value._int32 = date.Subtract(DateTime.MinValue).Days; - _isNull = false; - } - - internal void SetToTime(byte[] bytes, int length, byte scale, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Time; - FillInTimeInfo(ref _value._timeInfo, bytes, length, scale, denormalizedScale); - _isNull = false; - } - - internal void SetToTime(TimeSpan timeSpan, byte scale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.Time; - _value._timeInfo._ticks = timeSpan.Ticks; - _value._timeInfo._scale = scale; - _isNull = false; - } - - internal void SetToDateTime2(byte[] bytes, int length, byte scale, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTime2; - FillInTimeInfo(ref _value._dateTime2Info._timeInfo, bytes, length - 3, scale, denormalizedScale); // remaining 3 bytes is for date - _value._dateTime2Info._date = GetDateFromByteArray(bytes, length - 3); // 3 bytes for date - _isNull = false; - } - - internal void SetToDateTime2(DateTime dateTime, byte scale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTime2; - _value._dateTime2Info._timeInfo._ticks = dateTime.TimeOfDay.Ticks; - _value._dateTime2Info._timeInfo._scale = scale; - _value._dateTime2Info._date = dateTime.Subtract(DateTime.MinValue).Days; - _isNull = false; - } - - internal void SetToDateTimeOffset(byte[] bytes, int length, byte scale, byte denormalizedScale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTimeOffset; - FillInTimeInfo(ref _value._dateTimeOffsetInfo._dateTime2Info._timeInfo, bytes, length - 5, scale, denormalizedScale); // remaining 5 bytes are for date and offset - _value._dateTimeOffsetInfo._dateTime2Info._date = GetDateFromByteArray(bytes, length - 5); // 3 bytes for date - _value._dateTimeOffsetInfo._offset = (short)(bytes[length - 2] + (bytes[length - 1] << 8)); // 2 bytes for offset (Int16) - _isNull = false; - } - - internal void SetToDateTimeOffset(DateTimeOffset dateTimeOffset, byte scale) - { - Debug.Assert(IsEmpty, "setting value a second time?"); - - _type = StorageType.DateTimeOffset; - DateTime utcDateTime = dateTimeOffset.UtcDateTime; // timeInfo stores the utc datetime of a datatimeoffset - _value._dateTimeOffsetInfo._dateTime2Info._timeInfo._ticks = utcDateTime.TimeOfDay.Ticks; - _value._dateTimeOffsetInfo._dateTime2Info._timeInfo._scale = scale; - _value._dateTimeOffsetInfo._dateTime2Info._date = utcDateTime.Subtract(DateTime.MinValue).Days; - _value._dateTimeOffsetInfo._offset = (short)dateTimeOffset.Offset.TotalMinutes; - _isNull = false; - } - - private static void FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int length, byte scale, byte denormalizedScale) - { - Debug.Assert(3 <= length && length <= 5, "invalid data length for timeInfo: " + length); - Debug.Assert(0 <= scale && scale <= 7, "invalid scale: " + scale); - Debug.Assert(0 <= denormalizedScale && denormalizedScale <= 7, "invalid denormalized scale: " + denormalizedScale); - - long tickUnits = timeBytes[0] + ((long)timeBytes[1] << 8) + ((long)timeBytes[2] << 16); - if (length > 3) - { - tickUnits += ((long)timeBytes[3] << 24); - } - if (length > 4) - { - tickUnits += ((long)timeBytes[4] << 32); - } - timeInfo._ticks = tickUnits * TdsEnums.TICKS_FROM_SCALE[scale]; - - // Once the deserialization has been completed using the value scale, we need to set the actual denormalized scale, - // coming from the data type, on the original result, so that it has the proper scale setting. - // This only applies for values that got serialized/deserialized for encryption. Otherwise, both scales should be equal. - timeInfo._scale = denormalizedScale; - } - - private static int GetDateFromByteArray(byte[] buf, int offset) - { - return buf[offset] + (buf[offset + 1] << 8) + (buf[offset + 2] << 16); - } - - private void ThrowIfNull() - { - if (IsNull) - { - throw new SqlNullValueException(); - } - } - // [Field]As method explanation: - // these methods are used to bridge generic to non-generic access to value type fields on the storage struct - // where typeof(T) == typeof(field) - // 1) RyuJIT will recognize the pattern of (T)(object)T as being redundant and eliminate - // the T and object casts leaving T, so while this looks like it will put every value type instance in a box the - // generated assembly will be short and direct - // 2) another jit may not recognize the pattern and should emit the code as seen. this will box and then unbox the - // value type which is no worse than the mechanism that this code replaces - // where typeof(T) != typeof(field) - // the jit will emit all the cast operations as written. this will put the value into a box and then attempt to - // cast it, because it is an object no conversions are used and this will generate the desired InvalidCastException - // for example users cannot widen a short to an int preserving external expectations - - internal T ByteAs() - { - ThrowIfNull(); - return (T)(object)_value._byte; - } - - internal T BooleanAs() - { - ThrowIfNull(); - return (T)(object)_value._boolean; - } - - internal T Int32As() - { - ThrowIfNull(); - return (T)(object)_value._int32; - } - - internal T Int16As() - { - ThrowIfNull(); - return (T)(object)_value._int16; - } - - internal T Int64As() - { - ThrowIfNull(); - return (T)(object)_value._int64; - } - - internal T DoubleAs() - { - ThrowIfNull(); - return (T)(object)_value._double; - } - - internal T SingleAs() - { - ThrowIfNull(); - return (T)(object)_value._single; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs new file mode 100644 index 0000000000..2fc89dfde1 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlBuffer.netfx.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace Microsoft.Data.SqlClient +{ + internal sealed partial class SqlBuffer + { + internal void SetToDate(DateTime date) + { + Debug.Assert(IsEmpty, "setting value a second time?"); + + _type = StorageType.Date; + _value._int32 = date.Subtract(DateTime.MinValue).Days; + _isNull = false; + } + + internal void SetToDateTime2(DateTime dateTime, byte scale) + { + Debug.Assert(IsEmpty, "setting value a second time?"); + + _type = StorageType.DateTime2; + _value._dateTime2Info._timeInfo._ticks = dateTime.TimeOfDay.Ticks; + _value._dateTime2Info._timeInfo._scale = scale; + _value._dateTime2Info._date = dateTime.Subtract(DateTime.MinValue).Days; + _isNull = false; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientPermission.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientPermission.cs index 2d8b080be7..8c497f90d2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientPermission.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlClientPermission.cs @@ -15,23 +15,23 @@ namespace Microsoft.Data.SqlClient { - /// + /// [Serializable] public sealed class SqlClientPermission : System.Data.Common.DBDataPermission { - /// + /// [Obsolete("SqlClientPermission() has been deprecated. Use the SqlClientPermission(PermissionState.None) constructor. http://go.microsoft.com/fwlink/?linkid=14202", true)] // MDAC 86034 public SqlClientPermission() : this(PermissionState.None) { } - /// + /// public SqlClientPermission(PermissionState state) : base(state) { } - /// + /// [Obsolete("SqlClientPermission(PermissionState state, Boolean allowBlankPassword) has been deprecated. Use the SqlClientPermission(PermissionState.None) constructor. http://go.microsoft.com/fwlink/?linkid=14202", true)] // MDAC 86034 public SqlClientPermission(PermissionState state, bool allowBlankPassword) : this(state) { @@ -61,14 +61,14 @@ internal SqlClientPermission(SqlConnectionString constr) : base(PermissionState. } } - /// + /// public override void Add(string connectionString, string restrictions, KeyRestrictionBehavior behavior) { DBConnectionString constr = new DBConnectionString(connectionString, restrictions, behavior, SqlConnectionString.GetParseSynonyms(), false); AddPermissionEntry(constr); } - /// + /// override public IPermission Copy() { return new SqlClientPermission(this); @@ -126,7 +126,7 @@ private void CopyFrom(SqlClientPermission permission) } } - /// + /// override public IPermission Intersect(IPermission target) { // used during Deny actions if (null == target) @@ -179,7 +179,7 @@ private bool IsEmpty() return flag; } - /// + /// override public bool IsSubsetOf(IPermission target) { if (null == target) @@ -218,7 +218,7 @@ override public bool IsSubsetOf(IPermission target) return subset; } - /// + /// override public IPermission Union(IPermission target) { if (null == target) @@ -281,7 +281,7 @@ private string EncodeXmlValue(string value) // // // - /// + /// override public void FromXml(SecurityElement securityElement) { // code derived from CodeAccessPermission.ValidateElement @@ -344,7 +344,7 @@ override public void FromXml(SecurityElement securityElement) // // // - /// + /// override public SecurityElement ToXml() { Type type = this.GetType(); @@ -410,18 +410,18 @@ private static class XmlStr } - /// + /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)] [Serializable] public sealed class SqlClientPermissionAttribute : DBDataPermissionAttribute { - /// + /// public SqlClientPermissionAttribute(SecurityAction action) : base(action) { } - /// + /// override public IPermission CreatePermission() { return new SqlClientPermission(this); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.cs index bbe7e2f76b..e8dca4c858 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCertificateStoreProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlColumnEncryptionCertificateStoreProvider : SqlColumnEncryptionKeyStoreProvider { // Constants @@ -18,7 +18,7 @@ public class SqlColumnEncryptionCertificateStoreProvider : SqlColumnEncryptionKe // Assumption: Certificate Locations (LocalMachine & CurrentUser), Certificate Store name "My" // Certificate provider name (CertificateStore) dont need to be localized. - /// + /// public const string ProviderName = @"MSSQL_CERTIFICATE_STORE"; /// @@ -56,7 +56,7 @@ public class SqlColumnEncryptionCertificateStoreProvider : SqlColumnEncryptionKe /// private readonly byte[] _version = new byte[] { 0x01 }; - /// + /// public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters @@ -150,7 +150,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e return RSADecrypt(cipherText, certificate); } - /// + /// public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters @@ -240,7 +240,7 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e return encryptedColumnEncryptionKey; } - /// + /// public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { var hash = ComputeMasterKeyMetadataHash(masterKeyPath, allowEnclaveComputations, isSystemOp: false); @@ -253,7 +253,7 @@ public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool al return signature; } - /// + /// public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { var hash = ComputeMasterKeyMetadataHash(masterKeyPath, allowEnclaveComputations, isSystemOp: true); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs index bded38eeee..ad84233197 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCngProvider.cs @@ -9,10 +9,10 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlColumnEncryptionCngProvider : SqlColumnEncryptionKeyStoreProvider { - /// + /// public const string ProviderName = @"MSSQL_CNG_STORE"; /// @@ -26,7 +26,7 @@ public class SqlColumnEncryptionCngProvider : SqlColumnEncryptionKeyStoreProvide /// private readonly byte[] _version = new byte[] { 0x01 }; - /// + /// public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters @@ -120,7 +120,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e return RSADecrypt(rsaCngProvider, cipherText); } - /// + /// public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters @@ -210,13 +210,13 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e return encryptedColumnEncryptionKey; } - /// + /// public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw new NotSupportedException(); } - /// + /// public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw new NotSupportedException(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs index 33996913c2..6296d4005d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlColumnEncryptionCspProvider.cs @@ -10,10 +10,10 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlColumnEncryptionCspProvider : SqlColumnEncryptionKeyStoreProvider { - /// + /// public const string ProviderName = @"MSSQL_CSP_PROVIDER"; /// @@ -30,7 +30,7 @@ public class SqlColumnEncryptionCspProvider : SqlColumnEncryptionKeyStoreProvide /// private readonly byte[] _version = new byte[] { 0x01 }; - /// + /// public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey) { // Validate the input parameters @@ -124,7 +124,7 @@ public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string e return RSADecrypt(rsaProvider, cipherText); } - /// + /// public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey) { // Validate the input parameters @@ -214,13 +214,13 @@ public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string e return encryptedColumnEncryptionKey; } - /// + /// public override byte[] SignColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations) { throw new NotSupportedException(); } - /// + /// public override bool VerifyColumnMasterKeyMetadata(string masterKeyPath, bool allowEnclaveComputations, byte[] signature) { throw new NotSupportedException(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 1658944d87..7294c745d1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -40,6 +40,7 @@ namespace Microsoft.Data.SqlClient public sealed class SqlCommand : DbCommand, ICloneable { private static int _objectTypeCount; // EventSource Counter + private const int MaxRPCNameLength = 1046; internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); private string _commandText; @@ -160,10 +161,9 @@ internal bool IsColumnEncryptionEnabled } } - internal bool ShouldUseEnclaveBasedWorkflow - { - get { return !string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) && IsColumnEncryptionEnabled; } - } + internal bool ShouldUseEnclaveBasedWorkflow => + (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) || Connection.AttestationProtocol == SqlConnectionAttestationProtocol.None) && + IsColumnEncryptionEnabled; internal ConcurrentDictionary keysToBeSentToEnclave; internal bool requiresEnclaveComputations = false; @@ -1082,7 +1082,7 @@ public override void Prepare() { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG InternalPrepare(); } @@ -1267,7 +1267,7 @@ public override void Cancel() { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection); @@ -4780,7 +4780,7 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi if (isRequestedByEnclave) { - if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this.Connection.EnclaveAttestationUrl) && Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQuerySpDescribe(this._activeConnection.Parser.EnclaveType); } @@ -5244,8 +5244,11 @@ private void GenerateEnclavePackage() return; } - if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl)) + if (string.IsNullOrWhiteSpace(this._activeConnection.EnclaveAttestationUrl) && + Connection.AttestationProtocol != SqlConnectionAttestationProtocol.None) + { throw SQL.NoAttestationUrlSpecifiedForEnclaveBasedQueryGeneratingEnclavePackage(this._activeConnection.Parser.EnclaveType); + } string enclaveType = this._activeConnection.Parser.EnclaveType; if (string.IsNullOrWhiteSpace(enclaveType)) @@ -6525,7 +6528,6 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP int paramCount = GetParameterCount(parameters); int j = startCount; TdsParser parser = _activeConnection.Parser; - bool is2005OrNewer = parser.Is2005OrNewer; for (ii = 0; ii < paramCount; ii++) { @@ -6534,7 +6536,7 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP // func will change type to that with a 4 byte length if the type has a two // byte length and a parameter length > than that expressable in 2 bytes - if ((!parameter.ValidateTypeLengths(is2005OrNewer).IsPlp) && (parameter.Direction != ParameterDirection.Output)) + if ((!parameter.ValidateTypeLengths().IsPlp) && (parameter.Direction != ParameterDirection.Output)) { parameter.FixStreamDataForNonPLP(); } @@ -6690,7 +6692,19 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql int count = CountSendableParameters(parameters); GetRPCObject(count, ref rpc); - rpc.rpcName = this.CommandText; // just get the raw command text + // TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName + // 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523 + // each char takes 2 bytes. 523 * 2 = 1046 + int commandTextLength = ADP.CharSize * CommandText.Length; + + if (commandTextLength <= MaxRPCNameLength) + { + rpc.rpcName = CommandText; // just get the raw command text + } + else + { + throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength); + } SetUpRPCParameters(rpc, 0, inSchema, parameters); } @@ -6910,8 +6924,6 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete StringBuilder paramList = new StringBuilder(); bool fAddSeparator = false; - bool is2005OrNewer = parser.Is2005OrNewer; - int count = 0; count = parameters.Count; @@ -6961,7 +6973,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete { // func will change type to that with a 4 byte length if the type has a two // byte length and a parameter length > than that expressable in 2 bytes - mt = sqlParam.ValidateTypeLengths(is2005OrNewer); + mt = sqlParam.ValidateTypeLengths(); if ((!mt.IsPlp) && (sqlParam.Direction != ParameterDirection.Output)) { sqlParam.FixStreamDataForNonPLP(); @@ -7606,7 +7618,7 @@ private SmiRequestExecutor SetUpSmiRequest(SqlInternalConnectionSmi innerConnect if (innerConnection.Is2008OrNewer) { - ValueUtilsSmi.SetCompatibleValueV200(EventSink, requestExecutor, index, requestMetaData[index], value, typeCode, param.Offset, param.Size, peekAheadValues[index]); + ValueUtilsSmi.SetCompatibleValueV200(EventSink, requestExecutor, index, requestMetaData[index], value, typeCode, param.Offset, peekAheadValues[index]); } else { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 711ac3eaeb..04acfcb612 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -26,7 +26,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Data.ProviderBase; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [assembly: InternalsVisibleTo("System.Data.DataSetExtensions, PublicKey=" + Microsoft.Data.SqlClient.AssemblyRef.EcmaPublicKeyFull)] // DevDiv Bugs 92166 // NOTE: The current Microsoft.VSDesigner editor attributes are implemented for System.Data.SqlClient, and are not publicly available. @@ -36,7 +36,7 @@ namespace Microsoft.Data.SqlClient using System.Diagnostics.Tracing; using Microsoft.Data.Common; - /// + /// [ DefaultEvent("InfoMessage"), DesignerCategory("") @@ -88,7 +88,7 @@ static private readonly ConcurrentDictionary> _ColumnEncry capacity: 1, comparer: StringComparer.OrdinalIgnoreCase); - /// + /// [ DefaultValue(null), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -101,7 +101,7 @@ static private readonly ConcurrentDictionary> _ColumnEncry /// static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true; - /// + /// [ DefaultValue(null), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -124,7 +124,7 @@ static public bool ColumnEncryptionQueryMetadataCacheEnabled /// static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2); - /// + /// [ DefaultValue(null), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -136,7 +136,7 @@ static public TimeSpan ColumnEncryptionKeyCacheTtl set => _ColumnEncryptionKeyCacheTtl = value; } - /// + /// static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary customProviders) { ValidateCustomProviders(customProviders); @@ -340,12 +340,12 @@ public SqlRetryLogicBaseProvider RetryLogicProvider // using SqlConnection.Open() method. internal bool _applyTransientFaultHandling = false; - /// + /// public SqlConnection(string connectionString) : this(connectionString, null) { } - /// + /// public SqlConnection(string connectionString, SqlCredential credential) : this() { ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available @@ -432,9 +432,15 @@ private void CacheConnectionStringProperties() if (connString != null) { _connectRetryCount = connString.ConnectRetryCount; + // For Azure Synapse ondemand connections, set _connectRetryCount to 5 instead of 1 to greatly improve recovery + // success rate. Note: Synapse should be detected first as it could be detected as a regular Azure SQL DB endpoint. + if (_connectRetryCount == 1 && ADP.IsAzureSynapseOnDemandEndpoint(connString.DataSource)) + { + _connectRetryCount = 5; + } // For Azure SQL connection, set _connectRetryCount to 2 instead of 1 will greatly improve recovery - // success rate - if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) + // success rate + else if (_connectRetryCount == 1 && ADP.IsAzureSqlServerEndpoint(connString.DataSource)) { _connectRetryCount = 2; } @@ -445,7 +451,7 @@ private void CacheConnectionStringProperties() // PUBLIC PROPERTIES // - /// + /// // used to start/stop collection of statistics data and do verify the current state // // devnote: start/stop should not performed using a property since it requires execution of code @@ -683,7 +689,7 @@ internal int ConnectRetryInterval } } - /// + /// override protected DbProviderFactory DbProviderFactory { get @@ -693,7 +699,7 @@ override protected DbProviderFactory DbProviderFactory } // AccessToken: To be used for token based authentication - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -734,7 +740,7 @@ public string AccessToken } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(StringsHelper.ResourceNames.SqlConnection_ConnectionTimeout), @@ -748,7 +754,7 @@ public int CommandTimeout } } - /// + /// [ DefaultValue(""), #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute @@ -814,7 +820,7 @@ override public string ConnectionString } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(StringsHelper.ResourceNames.SqlConnection_ConnectionTimeout), @@ -828,7 +834,7 @@ override public int ConnectionTimeout } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResDescriptionAttribute(StringsHelper.ResourceNames.SqlConnection_Database), @@ -904,7 +910,7 @@ internal string SQLDNSCachingSupportedStateBeforeRedirect } } - /// + /// [ Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -930,7 +936,7 @@ override public string DataSource } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -964,7 +970,7 @@ public int PacketSize } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -995,7 +1001,7 @@ public Guid ClientConnectionId } } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -1021,7 +1027,7 @@ public int ServerProcessId GetOpenTdsConnection().ServerProcessId : 0; } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -1049,7 +1055,7 @@ internal SqlStatistics Statistics } } - /// + /// [ DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_Data), @@ -1079,7 +1085,7 @@ public string WorkstationId } } - /// + /// // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication [ Browsable(false), @@ -1218,7 +1224,7 @@ private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(S // PUBLIC EVENTS // - /// + /// [ ResCategoryAttribute(StringsHelper.ResourceNames.DataCategory_InfoMessage), ResDescriptionAttribute(StringsHelper.ResourceNames.DbConnection_InfoMessage), @@ -1235,7 +1241,7 @@ public event SqlInfoMessageEventHandler InfoMessage } } - /// + /// public bool FireInfoMessageEventOnUserErrors { get @@ -1299,21 +1305,21 @@ internal int ReconnectCount // // PUBLIC METHODS // - /// + /// new public SqlTransaction BeginTransaction() { // this is just a delegate. The actual method tracks executiontime return BeginTransaction(IsolationLevel.Unspecified, null); } - /// + /// new public SqlTransaction BeginTransaction(IsolationLevel iso) { // this is just a delegate. The actual method tracks executiontime return BeginTransaction(iso, null); } - /// + /// public SqlTransaction BeginTransaction(string transactionName) { // Use transaction names only on the outermost pair of nested @@ -1323,7 +1329,7 @@ public SqlTransaction BeginTransaction(string transactionName) return BeginTransaction(IsolationLevel.Unspecified, transactionName); } - /// + /// // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355) [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")] override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) @@ -1343,7 +1349,7 @@ override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLeve } } - /// + /// public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) { WaitForPendingReconnection(); @@ -1381,7 +1387,7 @@ public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionNam } } - /// + /// override public void ChangeDatabase(string database) { SqlStatistics statistics = null; @@ -1435,14 +1441,14 @@ override public void ChangeDatabase(string database) } } - /// + /// static public void ClearAllPools() { (new SqlClientPermission(PermissionState.Unrestricted)).Demand(); SqlConnectionFactory.SingletonInstance.ClearAllPools(); } - /// + /// static public void ClearPool(SqlConnection connection) { ADP.CheckArgumentNull(connection, "connection"); @@ -1459,7 +1465,7 @@ static public void ClearPool(SqlConnection connection) } } - /// + /// object ICloneable.Clone() { SqlConnection clone = new SqlConnection(this); @@ -1477,7 +1483,7 @@ void CloseInnerConnection() InnerConnection.CloseConnection(this, ConnectionFactory); } - /// + /// override public void Close() { using (TryEventScope.Create(" {0}", ObjectID)) @@ -1573,7 +1579,7 @@ override public void Close() } } - /// + /// new public SqlCommand CreateCommand() { return new SqlCommand(null, this); @@ -1604,7 +1610,7 @@ private void DisposeMe(bool disposing) } } - /// + /// public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction transaction) { if (IsContextConnection) @@ -1615,7 +1621,7 @@ public void EnlistDistributedTransaction(System.EnterpriseServices.ITransaction EnlistDistributedTransactionHelper(transaction); } - /// + /// override public void Open() { Open(SqlConnectionOverrides.None); @@ -1881,7 +1887,7 @@ void CancelOpenAndWait() private Task InternalOpenWithRetryAsync(CancellationToken cancellationToken) => RetryLogicProvider.ExecuteAsync(this, () => InternalOpenAsync(cancellationToken), cancellationToken); - /// + /// public override Task OpenAsync(CancellationToken cancellationToken) => IsProviderRetriable ? InternalOpenWithRetryAsync(cancellationToken) : @@ -2664,7 +2670,7 @@ private void IssueSQLDebug(uint option, string machineName, uint pid, uint id, s c.ExecuteNonQuery(); } - /// + /// public static void ChangePassword(string connectionString, string newPassword) { using (TryEventScope.Create("")) @@ -2707,7 +2713,7 @@ public static void ChangePassword(string connectionString, string newPassword) } } - /// + /// public static void ChangePassword(string connectionString, SqlCredential credential, SecureString newSecurePassword) { using (TryEventScope.Create("")) @@ -2816,7 +2822,7 @@ static private void RefreshMemoryMappedData(SqlDebugContext sdc) sdc.data = memMap.rgbData; } - /// + /// public void ResetStatistics() { if (IsContextConnection) @@ -2835,7 +2841,7 @@ public void ResetStatistics() } } - /// + /// public IDictionary RetrieveStatistics() { if (IsContextConnection) @@ -2865,7 +2871,7 @@ private void UpdateStatistics() Statistics.UpdateStatistics(); } - /// + /// public IDictionary RetrieveInternalInfo() { IDictionary internalDictionary = new Dictionary(); @@ -2950,7 +2956,7 @@ internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnD MemoryStream stm = new MemoryStream((byte[])value); - o = SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); + o = Server.SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); Debug.Assert(o != null, "object could NOT be created"); return o; @@ -2979,7 +2985,7 @@ internal byte[] GetBytes(object o, out Format format, out int maxSize) using (MemoryStream stm = new MemoryStream(maxSize < 0 ? 0 : maxSize)) { - SerializationHelperSql9.Serialize(stm, o); + Server.SerializationHelperSql9.Serialize(stm, o); retval = stm.ToArray(); } return retval; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs index eb41758480..07d44f020c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs @@ -28,7 +28,7 @@ public sealed partial class SqlConnection : DbConnection private static int _objectTypeCount; // EventSource Counter internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - /// + /// public SqlConnection() : base() { GC.SuppressFinalize(this); @@ -197,7 +197,7 @@ internal void AddWeakReference(object value, int tag) InnerConnection.AddWeakReference(value, tag); } - /// + /// override protected DbCommand CreateDbCommand() { using (TryEventScope.Create(" {0}", ObjectID)) @@ -216,7 +216,7 @@ private static System.Security.CodeAccessPermission CreateExecutePermission() return p; } - /// + /// override protected void Dispose(bool disposing) { if (disposing) @@ -262,7 +262,7 @@ private void EnlistDistributedTransactionHelper(System.EnterpriseServices.ITrans GC.KeepAlive(this); } - /// + /// override public void EnlistTransaction(SysTx.Transaction transaction) { SqlConnection.ExecutePermission.Demand(); @@ -312,19 +312,19 @@ internal DbMetaDataFactory GetMetaDataFactoryInternal(DbConnectionInternal inter return GetMetaDataFactory(internalConnection); } - /// + /// override public DataTable GetSchema() { return this.GetSchema(DbMetaDataCollectionNames.MetaDataCollections, null); } - /// + /// override public DataTable GetSchema(string collectionName) { return this.GetSchema(collectionName, null); } - /// + /// override public DataTable GetSchema(string collectionName, string[] restrictionValues) { // NOTE: This is virtual because not all providers may choose to support diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index c574b146e6..d081aab9b6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -24,7 +24,7 @@ namespace Microsoft.Data.SqlClient { - /// + /// public class SqlDataReader : DbDataReader, IDataReader { private enum ALTROWSTATUS @@ -143,7 +143,7 @@ internal SqlCommand Command } } - /// + /// protected SqlConnection Connection { get @@ -152,10 +152,10 @@ protected SqlConnection Connection } } - /// + /// public SensitivityClassification SensitivityClassification { get; internal set; } - /// + /// override public int Depth { get @@ -169,7 +169,7 @@ override public int Depth } } - /// + /// // fields/attributes collection override public int FieldCount { @@ -193,7 +193,7 @@ override public int FieldCount } } - /// + /// override public bool HasRows { get @@ -211,7 +211,7 @@ override public bool HasRows } } - /// + /// override public bool IsClosed { get @@ -405,7 +405,7 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() return metaDataReturn; } - /// + /// override public int RecordsAffected { get @@ -445,7 +445,7 @@ internal MultiPartTableName[] TableNames _tableNames = value; } } - /// + /// override public int VisibleFieldCount { get @@ -463,7 +463,7 @@ override public int VisibleFieldCount } } - /// + /// // this operator override public object this[int i] { @@ -473,7 +473,7 @@ override public object this[int i] } } - /// + /// override public object this[string name] { get @@ -951,7 +951,7 @@ protected override void Dispose(bool disposing) } } - /// + /// override public void Close() { SqlStatistics statistics = null; @@ -1357,7 +1357,7 @@ private bool TryConsumeMetaData() return true; } - /// + /// override public string GetDataTypeName(int i) { SqlStatistics statistics = null; @@ -1434,13 +1434,13 @@ virtual internal SqlBuffer.StorageType GetVariantInternalStorageType(int i) return _data[i].VariantInternalStorageType; } - /// + /// override public IEnumerator GetEnumerator() { return new DbEnumerator(this, IsCommandBehavior(CommandBehavior.CloseConnection)); } - /// + /// override public Type GetFieldType(int i) { SqlStatistics statistics = null; @@ -1543,7 +1543,7 @@ virtual internal int GetLocaleId(int i) return lcid; } - /// + /// override public string GetName(int i) { CheckMetaDataIsReady(columnIndex: i); @@ -1552,7 +1552,7 @@ override public string GetName(int i) return _metaData[i].column; } - /// + /// override public Type GetProviderSpecificFieldType(int i) { SqlStatistics statistics = null; @@ -1624,7 +1624,7 @@ private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) return providerSpecificFieldType; } - /// + /// // named field access override public int GetOrdinal(string name) { @@ -1645,19 +1645,19 @@ override public int GetOrdinal(string name) } } - /// + /// override public object GetProviderSpecificValue(int i) { return GetSqlValue(i); } - /// + /// override public int GetProviderSpecificValues(object[] values) { return GetSqlValues(values); } - /// + /// override public DataTable GetSchemaTable() { SqlStatistics statistics = null; @@ -1683,14 +1683,14 @@ override public DataTable GetSchemaTable() } } - /// + /// override public bool GetBoolean(int i) { ReadColumn(i); return _data[i].Boolean; } - /// + /// virtual public XmlReader GetXmlReader(int i) { // NOTE: sql_variant can not contain a XML data type: http://msdn.microsoft.com/en-us/library/ms173829.aspx @@ -1730,7 +1730,7 @@ virtual public XmlReader GetXmlReader(int i) } } - /// + /// override public Stream GetStream(int i) { CheckDataIsReady(columnIndex: i, methodName: "GetStream"); @@ -1778,14 +1778,14 @@ override public Stream GetStream(int i) } } - /// + /// override public byte GetByte(int i) { ReadColumn(i); return _data[i].Byte; } - /// + /// override public long GetBytes(int i, long dataIndex, byte[] buffer, int bufferIndex, int length) { SqlStatistics statistics = null; @@ -2022,7 +2022,7 @@ private bool TryGetBytesInternal(int i, long dataIndex, byte[] buffer, int buffe cbytes = length; } - Array.Copy(data, ndataIndex, buffer, bufferIndex, cbytes); + Buffer.BlockCopy(data, ndataIndex, buffer, bufferIndex, cbytes); } catch (Exception e) { @@ -2175,7 +2175,7 @@ internal bool TryGetBytesInternalSequential(int i, byte[] buffer, int index, int { // Read data (not exceeding the total amount of data available) int bytesToRead = (int)Math.Min((long)length, _sharedState._columnDataBytesRemaining); - bool result = _stateObj.TryReadByteArray(buffer, index, bytesToRead, out bytesRead); + bool result = _stateObj.TryReadByteArray(buffer.AsSpan(start: index), bytesToRead, out bytesRead); _columnDataBytesRead += bytesRead; _sharedState._columnDataBytesRemaining -= bytesRead; return result; @@ -2218,7 +2218,7 @@ internal bool TryGetBytesInternalSequential(int i, byte[] buffer, int index, int } } - /// + /// override public TextReader GetTextReader(int i) { CheckDataIsReady(columnIndex: i, methodName: "GetTextReader"); @@ -2288,14 +2288,14 @@ override public TextReader GetTextReader(int i) } } - /// + /// [EditorBrowsableAttribute(EditorBrowsableState.Never)] // MDAC 69508 override public char GetChar(int i) { throw ADP.NotSupported(); } - /// + /// override public long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) { SqlStatistics statistics = null; @@ -2637,14 +2637,14 @@ internal long GetStreamingXmlChars(int i, long dataIndex, char[] buffer, int buf return cnt; } - /// + /// [EditorBrowsableAttribute(EditorBrowsableState.Never)] // MDAC 69508 IDataReader IDataRecord.GetData(int i) { throw ADP.NotSupported(); } - /// + /// override public DateTime GetDateTime(int i) { ReadColumn(i); @@ -2666,77 +2666,77 @@ override public DateTime GetDateTime(int i) return dt; } - /// + /// override public Decimal GetDecimal(int i) { ReadColumn(i); return _data[i].Decimal; } - /// + /// override public double GetDouble(int i) { ReadColumn(i); return _data[i].Double; } - /// + /// override public float GetFloat(int i) { ReadColumn(i); return _data[i].Single; } - /// + /// override public Guid GetGuid(int i) { ReadColumn(i); return _data[i].Guid; } - /// + /// override public Int16 GetInt16(int i) { ReadColumn(i); return _data[i].Int16; } - /// + /// override public Int32 GetInt32(int i) { ReadColumn(i); return _data[i].Int32; } - /// + /// override public Int64 GetInt64(int i) { ReadColumn(i); return _data[i].Int64; } - /// + /// virtual public SqlBoolean GetSqlBoolean(int i) { ReadColumn(i); return _data[i].SqlBoolean; } - /// + /// virtual public SqlBinary GetSqlBinary(int i) { ReadColumn(i, setTimeout: true, allowPartiallyReadColumn: true); return _data[i].SqlBinary; } - /// + /// virtual public SqlByte GetSqlByte(int i) { ReadColumn(i); return _data[i].SqlByte; } - /// + /// virtual public SqlBytes GetSqlBytes(int i) { ReadColumn(i); @@ -2744,7 +2744,7 @@ virtual public SqlBytes GetSqlBytes(int i) return new SqlBytes(data); } - /// + /// virtual public SqlChars GetSqlChars(int i) { ReadColumn(i); @@ -2761,70 +2761,70 @@ virtual public SqlChars GetSqlChars(int i) return new SqlChars(data); } - /// + /// virtual public SqlDateTime GetSqlDateTime(int i) { ReadColumn(i); return _data[i].SqlDateTime; } - /// + /// virtual public SqlDecimal GetSqlDecimal(int i) { ReadColumn(i); return _data[i].SqlDecimal; } - /// + /// virtual public SqlGuid GetSqlGuid(int i) { ReadColumn(i); return _data[i].SqlGuid; } - /// + /// virtual public SqlDouble GetSqlDouble(int i) { ReadColumn(i); return _data[i].SqlDouble; } - /// + /// virtual public SqlInt16 GetSqlInt16(int i) { ReadColumn(i); return _data[i].SqlInt16; } - /// + /// virtual public SqlInt32 GetSqlInt32(int i) { ReadColumn(i); return _data[i].SqlInt32; } - /// + /// virtual public SqlInt64 GetSqlInt64(int i) { ReadColumn(i); return _data[i].SqlInt64; } - /// + /// virtual public SqlMoney GetSqlMoney(int i) { ReadColumn(i); return _data[i].SqlMoney; } - /// + /// virtual public SqlSingle GetSqlSingle(int i) { ReadColumn(i); return _data[i].SqlSingle; } - /// + /// // UNDONE: need non-unicode SqlString support virtual public SqlString GetSqlString(int i) { @@ -2838,7 +2838,7 @@ virtual public SqlString GetSqlString(int i) return _data[i].SqlString; } - /// + /// virtual public SqlXml GetSqlXml(int i) { ReadColumn(i); @@ -2869,7 +2869,7 @@ virtual public SqlXml GetSqlXml(int i) return sx; } - /// + /// virtual public object GetSqlValue(int i) { SqlStatistics statistics = null; @@ -2955,7 +2955,7 @@ private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met } } - /// + /// virtual public int GetSqlValues(object[] values) { SqlStatistics statistics = null; @@ -2984,7 +2984,7 @@ virtual public int GetSqlValues(object[] values) } } - /// + /// override public string GetString(int i) { ReadColumn(i); @@ -2998,7 +2998,7 @@ override public string GetString(int i) return _data[i].String; } - /// + /// override public T GetFieldValue(int i) { SqlStatistics statistics = null; @@ -3015,7 +3015,7 @@ override public T GetFieldValue(int i) } } - /// + /// override public object GetValue(int i) { SqlStatistics statistics = null; @@ -3032,7 +3032,7 @@ override public object GetValue(int i) } } - /// + /// virtual public TimeSpan GetTimeSpan(int i) { ReadColumn(i); @@ -3054,7 +3054,7 @@ virtual public TimeSpan GetTimeSpan(int i) return t; } - /// + /// virtual public DateTimeOffset GetDateTimeOffset(int i) { ReadColumn(i); @@ -3357,7 +3357,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met } } - /// + /// override public int GetValues(object[] values) { SqlStatistics statistics = null; @@ -3651,7 +3651,7 @@ private bool IsRowToken(byte token) return TdsEnums.SQLROW == token || TdsEnums.SQLNBCROW == token; } - /// + /// override public bool IsDBNull(int i) { if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && ((_sharedState._nextColumnHeaderToRead > i + 1) || (_lastColumnWithDataChunkRead > i))) @@ -3676,13 +3676,13 @@ override public bool IsDBNull(int i) return _data[i].IsNull; } - /// + /// protected internal bool IsCommandBehavior(CommandBehavior condition) { return (condition == (condition & _commandBehavior)); } - /// + /// override public bool NextResult() { if (_currentTask != null) @@ -3888,7 +3888,7 @@ private bool TryNextResult(out bool more) } } - /// + /// // user must call Read() to position on the first row override public bool Read() { @@ -4962,7 +4962,7 @@ private void AssertReaderState(bool requireData, bool permitAsync, int? columnIn } } - /// + /// public override Task NextResultAsync(CancellationToken cancellationToken) { using (TryEventScope.Create(" {0}", ObjectID)) @@ -5299,7 +5299,7 @@ out bytesRead return null; } - /// + /// public override Task ReadAsync(CancellationToken cancellationToken) { using (TryEventScope.Create(" {0}", ObjectID)) @@ -5475,7 +5475,7 @@ private void SetCachedReadAsyncCallContext(ReadAsyncCallContext instance) Interlocked.CompareExchange(ref _cachedReadAsyncContext, instance, null); } - /// + /// override public Task IsDBNullAsync(int i, CancellationToken cancellationToken) { @@ -5613,7 +5613,7 @@ private void SetCachedIDBNullAsyncCallContext(IsDBNullAsyncCallContext instance) Interlocked.CompareExchange(ref _cachedIsDBNullContext, instance, null); } - /// + /// override public Task GetFieldValueAsync(int i, CancellationToken cancellationToken) { try diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReaderSmi.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReaderSmi.cs index 8926b74dd8..f58a8c19bd 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReaderSmi.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReaderSmi.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -35,7 +36,7 @@ public override int FieldCount { get { - ThrowIfClosed("FieldCount"); + ThrowIfClosed(); return InternalFieldCount; } } @@ -44,7 +45,7 @@ public override int VisibleFieldCount { get { - ThrowIfClosed("VisibleFieldCount"); + ThrowIfClosed(); if (FNotInResults()) { @@ -58,15 +59,15 @@ public override int VisibleFieldCount // // IDBRecord Metadata Methods // - public override String GetName(int ordinal) + public override string GetName(int ordinal) { - EnsureCanGetMetaData("GetName"); + EnsureCanGetMetaData(); return _currentMetaData[ordinal].Name; } - public override String GetDataTypeName(int ordinal) + public override string GetDataTypeName(int ordinal) { - EnsureCanGetMetaData("GetDataTypeName"); + EnsureCanGetMetaData(); SmiExtendedMetaData md = _currentMetaData[ordinal]; if (SqlDbType.Udt == md.SqlDbType) { @@ -80,21 +81,20 @@ public override String GetDataTypeName(int ordinal) public override Type GetFieldType(int ordinal) { - EnsureCanGetMetaData("GetFieldType"); - if (SqlDbType.Udt == _currentMetaData[ordinal].SqlDbType) + EnsureCanGetMetaData(); + if (_currentMetaData[ordinal].SqlDbType == SqlDbType.Udt) { return _currentMetaData[ordinal].Type; } else { - return MetaType.GetMetaTypeFromSqlDbType( - _currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).ClassType; + return MetaType.GetMetaTypeFromSqlDbType(_currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).ClassType; } } override public Type GetProviderSpecificFieldType(int ordinal) { - EnsureCanGetMetaData("GetProviderSpecificFieldType"); + EnsureCanGetMetaData(); if (SqlDbType.Udt == _currentMetaData[ordinal].SqlDbType) { @@ -102,8 +102,7 @@ override public Type GetProviderSpecificFieldType(int ordinal) } else { - return MetaType.GetMetaTypeFromSqlDbType( - _currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).SqlType; + return MetaType.GetMetaTypeFromSqlDbType(_currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).SqlType; } } @@ -111,14 +110,14 @@ public override int Depth { get { - ThrowIfClosed("Depth"); + ThrowIfClosed(); return 0; } } // UNDONE: (alazela 10/14/2001) Multi-level reader not impl. - public override Object GetValue(int ordinal) + public override object GetValue(int ordinal) { - EnsureCanGetCol("GetValue", ordinal); + EnsureCanGetCol(ordinal); SmiQueryMetaData metaData = _currentMetaData[ordinal]; if (_currentConnection.Is2008OrNewer) { @@ -132,7 +131,7 @@ public override Object GetValue(int ordinal) public override T GetFieldValue(int ordinal) { - EnsureCanGetCol("GetFieldValue", ordinal); + EnsureCanGetCol(ordinal); SmiQueryMetaData metaData = _currentMetaData[ordinal]; if (typeof(INullable).IsAssignableFrom(typeof(T))) @@ -171,21 +170,27 @@ override internal SqlBuffer.StorageType GetVariantInternalStorageType(int ordina { Debug.Assert(null != _currentColumnValuesV3, "Attempting to get variant internal storage type without calling GetValue first"); if (IsDBNull(ordinal)) + { return SqlBuffer.StorageType.Empty; + } SmiMetaData valueMetaData = _currentColumnValuesV3.GetVariantType(_readerEventSink, ordinal); if (valueMetaData == null) + { return SqlBuffer.StorageType.Empty; + } else + { return ValueUtilsSmi.SqlDbTypeToStorageType(valueMetaData.SqlDbType); + } } public override int GetValues(object[] values) { - EnsureCanGetCol("GetValues", 0); - if (null == values) + EnsureCanGetCol(0); + if (values == null) { - throw ADP.ArgumentNull("values"); + throw ADP.ArgumentNull(nameof(values)); } int copyLength = (values.Length < _visibleColumnCount) ? values.Length : _visibleColumnCount; @@ -198,8 +203,8 @@ public override int GetValues(object[] values) public override int GetOrdinal(string name) { - EnsureCanGetMetaData("GetOrdinal"); - if (null == _fieldNameLookup) + EnsureCanGetMetaData(); + if (_fieldNameLookup == null) { _fieldNameLookup = new FieldNameLookup((IDataReader)this, -1); // TODO: Need to support DefaultLCID for name comparisons } @@ -207,29 +212,17 @@ public override int GetOrdinal(string name) } // Generic array access by column index (accesses column value) - public override object this[int ordinal] - { - get - { - return GetValue(ordinal); - } - } + public override object this[int ordinal] => GetValue(ordinal); // Generic array access by column name (accesses column value) - public override object this[string strName] - { - get - { - return GetValue(GetOrdinal(strName)); - } - } + public override object this[string strName] => GetValue(GetOrdinal(strName)); // // IDataRecord Data Access methods // public override bool IsDBNull(int ordinal) { - EnsureCanGetCol("IsDBNull", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal); } @@ -241,37 +234,34 @@ public override Task IsDBNullAsync(int ordinal, CancellationToken cancella public override bool GetBoolean(int ordinal) { - EnsureCanGetCol("GetBoolean", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetBoolean(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override byte GetByte(int ordinal) { - EnsureCanGetCol("GetByte", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetByte(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { - EnsureCanGetCol("GetBytes", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetBytes(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], fieldOffset, buffer, bufferOffset, length, true); } // XmlReader support code calls this method. internal override long GetBytesInternal(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) { - EnsureCanGetCol("GetBytes", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetBytesInternal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], fieldOffset, buffer, bufferOffset, length, false); } - public override char GetChar(int ordinal) - { - throw ADP.NotSupported(); - } + public override char GetChar(int ordinal) => throw ADP.NotSupported(); public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) { - EnsureCanGetCol("GetChars", ordinal); + EnsureCanGetCol(ordinal); SmiExtendedMetaData metaData = _currentMetaData[ordinal]; if (IsCommandBehavior(CommandBehavior.SequentialAccess)) { @@ -285,55 +275,55 @@ public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int public override Guid GetGuid(int ordinal) { - EnsureCanGetCol("GetGuid", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetGuid(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Int16 GetInt16(int ordinal) + public override short GetInt16(int ordinal) { - EnsureCanGetCol("GetInt16", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetInt16(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Int32 GetInt32(int ordinal) + public override int GetInt32(int ordinal) { - EnsureCanGetCol("GetInt32", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetInt32(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Int64 GetInt64(int ordinal) + public override long GetInt64(int ordinal) { - EnsureCanGetCol("GetInt64", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetInt64(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Single GetFloat(int ordinal) + public override float GetFloat(int ordinal) { - EnsureCanGetCol("GetFloat", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSingle(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Double GetDouble(int ordinal) + public override double GetDouble(int ordinal) { - EnsureCanGetCol("GetDouble", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetDouble(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override String GetString(int ordinal) + public override string GetString(int ordinal) { - EnsureCanGetCol("GetString", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetString(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } - public override Decimal GetDecimal(int ordinal) + public override decimal GetDecimal(int ordinal) { - EnsureCanGetCol("GetDecimal", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetDecimal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override DateTime GetDateTime(int ordinal) { - EnsureCanGetCol("GetDateTime", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetDateTime(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } @@ -341,21 +331,9 @@ public override DateTime GetDateTime(int ordinal) // IDataReader properties // // Logically closed test. I.e. is this object closed as far as external access is concerned? - public override bool IsClosed - { - get - { - return IsReallyClosed(); - } - } + public override bool IsClosed => IsReallyClosed(); - public override int RecordsAffected - { - get - { - return base.Command.InternalRecordsAffected; - } - } + public override int RecordsAffected => Command.InternalRecordsAffected; // // IDataReader methods @@ -418,7 +396,7 @@ private void CloseInternal(bool closeConnection) // Move to the next resultset public override unsafe bool NextResult() { - ThrowIfClosed("NextResult"); + ThrowIfClosed(); bool hasAnotherResult = InternalNextResult(false); @@ -455,7 +433,7 @@ internal unsafe bool InternalNextResult(bool ignoreNonFatalMessages) // result, the metadata for it will be available after the last // read on the prior result. - while (null == _currentMetaData && _eventStream.HasEvents) + while (_currentMetaData == null && _eventStream.HasEvents) { _eventStream.ProcessEvent(_readerEventSink); _readerEventSink.ProcessMessagesAndThrow(ignoreNonFatalMessages); @@ -472,7 +450,7 @@ internal unsafe bool InternalNextResult(bool ignoreNonFatalMessages) public override bool Read() { - ThrowIfClosed("Read"); + ThrowIfClosed(); bool hasAnotherRow = InternalRead(false); return hasAnotherRow; @@ -510,8 +488,8 @@ internal unsafe bool InternalRead(bool ignoreNonFatalErrors) } // NOTE: SQLBUDT #386118 -- may indicate that we want to break this loop when we get a MessagePosted callback, but we can't prove that. - while (null == _currentColumnValues && // Did we find a row? - null == _currentColumnValuesV3 && // Did we find a V3 row? + while (_currentColumnValues == null && // Did we find a row? + _currentColumnValuesV3 == null && // Did we find a V3 row? FInResults() && // Was the batch terminated due to a serious error? PositionState.AfterRows != _currentPosition && // Have we seen a statement completed event? _eventStream.HasEvents) @@ -531,55 +509,57 @@ internal unsafe bool InternalRead(bool ignoreNonFatalErrors) public override DataTable GetSchemaTable() { - ThrowIfClosed("GetSchemaTable"); + ThrowIfClosed(); - if (null == _schemaTable && FInResults()) + if (_schemaTable == null && FInResults()) { - DataTable schemaTable = new DataTable("SchemaTable"); - schemaTable.Locale = System.Globalization.CultureInfo.InvariantCulture; - schemaTable.MinimumCapacity = InternalFieldCount; - - DataColumn ColumnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(System.String)); - DataColumn Ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(System.Int32)); - DataColumn Size = new DataColumn(SchemaTableColumn.ColumnSize, typeof(System.Int32)); - DataColumn Precision = new DataColumn(SchemaTableColumn.NumericPrecision, typeof(System.Int16)); - DataColumn Scale = new DataColumn(SchemaTableColumn.NumericScale, typeof(System.Int16)); - - DataColumn DataType = new DataColumn(SchemaTableColumn.DataType, typeof(System.Type)); - DataColumn ProviderSpecificDataType = new DataColumn(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type)); - DataColumn ProviderType = new DataColumn(SchemaTableColumn.ProviderType, typeof(System.Int32)); - DataColumn NonVersionedProviderType = new DataColumn(SchemaTableColumn.NonVersionedProviderType, typeof(System.Int32)); - - DataColumn IsLong = new DataColumn(SchemaTableColumn.IsLong, typeof(System.Boolean)); - DataColumn AllowDBNull = new DataColumn(SchemaTableColumn.AllowDBNull, typeof(System.Boolean)); - DataColumn IsReadOnly = new DataColumn(SchemaTableOptionalColumn.IsReadOnly, typeof(System.Boolean)); - DataColumn IsRowVersion = new DataColumn(SchemaTableOptionalColumn.IsRowVersion, typeof(System.Boolean)); - - DataColumn IsUnique = new DataColumn(SchemaTableColumn.IsUnique, typeof(System.Boolean)); - DataColumn IsKey = new DataColumn(SchemaTableColumn.IsKey, typeof(System.Boolean)); - DataColumn IsAutoIncrement = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement, typeof(System.Boolean)); - DataColumn IsHidden = new DataColumn(SchemaTableOptionalColumn.IsHidden, typeof(System.Boolean)); - - DataColumn BaseCatalogName = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName, typeof(System.String)); - DataColumn BaseSchemaName = new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(System.String)); - DataColumn BaseTableName = new DataColumn(SchemaTableColumn.BaseTableName, typeof(System.String)); - DataColumn BaseColumnName = new DataColumn(SchemaTableColumn.BaseColumnName, typeof(System.String)); + DataTable schemaTable = new DataTable("SchemaTable") + { + Locale = System.Globalization.CultureInfo.InvariantCulture, + MinimumCapacity = InternalFieldCount + }; + + DataColumn ColumnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(string)); + DataColumn Ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(int)); + DataColumn Size = new DataColumn(SchemaTableColumn.ColumnSize, typeof(int)); + DataColumn Precision = new DataColumn(SchemaTableColumn.NumericPrecision, typeof(short)); + DataColumn Scale = new DataColumn(SchemaTableColumn.NumericScale, typeof(short)); + + DataColumn DataType = new DataColumn(SchemaTableColumn.DataType, typeof(Type)); + DataColumn ProviderSpecificDataType = new DataColumn(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(Type)); + DataColumn ProviderType = new DataColumn(SchemaTableColumn.ProviderType, typeof(int)); + DataColumn NonVersionedProviderType = new DataColumn(SchemaTableColumn.NonVersionedProviderType, typeof(int)); + + DataColumn IsLong = new DataColumn(SchemaTableColumn.IsLong, typeof(bool)); + DataColumn AllowDBNull = new DataColumn(SchemaTableColumn.AllowDBNull, typeof(bool)); + DataColumn IsReadOnly = new DataColumn(SchemaTableOptionalColumn.IsReadOnly, typeof(bool)); + DataColumn IsRowVersion = new DataColumn(SchemaTableOptionalColumn.IsRowVersion, typeof(bool)); + + DataColumn IsUnique = new DataColumn(SchemaTableColumn.IsUnique, typeof(bool)); + DataColumn IsKey = new DataColumn(SchemaTableColumn.IsKey, typeof(bool)); + DataColumn IsAutoIncrement = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement, typeof(bool)); + DataColumn IsHidden = new DataColumn(SchemaTableOptionalColumn.IsHidden, typeof(bool)); + + DataColumn BaseCatalogName = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName, typeof(string)); + DataColumn BaseSchemaName = new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(string)); + DataColumn BaseTableName = new DataColumn(SchemaTableColumn.BaseTableName, typeof(string)); + DataColumn BaseColumnName = new DataColumn(SchemaTableColumn.BaseColumnName, typeof(string)); // unique to SqlClient - DataColumn BaseServerName = new DataColumn(SchemaTableOptionalColumn.BaseServerName, typeof(System.String)); - DataColumn IsAliased = new DataColumn(SchemaTableColumn.IsAliased, typeof(System.Boolean)); - DataColumn IsExpression = new DataColumn(SchemaTableColumn.IsExpression, typeof(System.Boolean)); - DataColumn IsIdentity = new DataColumn("IsIdentity", typeof(System.Boolean)); + DataColumn BaseServerName = new DataColumn(SchemaTableOptionalColumn.BaseServerName, typeof(string)); + DataColumn IsAliased = new DataColumn(SchemaTableColumn.IsAliased, typeof(bool)); + DataColumn IsExpression = new DataColumn(SchemaTableColumn.IsExpression, typeof(bool)); + DataColumn IsIdentity = new DataColumn("IsIdentity", typeof(bool)); // UDT specific. Holds UDT typename ONLY if the type of the column is UDT, otherwise the data type - DataColumn DataTypeName = new DataColumn("DataTypeName", typeof(System.String)); - DataColumn UdtAssemblyQualifiedName = new DataColumn("UdtAssemblyQualifiedName", typeof(System.String)); + DataColumn DataTypeName = new DataColumn("DataTypeName", typeof(string)); + DataColumn UdtAssemblyQualifiedName = new DataColumn("UdtAssemblyQualifiedName", typeof(string)); // Xml metadata specific - DataColumn XmlSchemaCollectionDatabase = new DataColumn("XmlSchemaCollectionDatabase", typeof(System.String)); - DataColumn XmlSchemaCollectionOwningSchema = new DataColumn("XmlSchemaCollectionOwningSchema", typeof(System.String)); - DataColumn XmlSchemaCollectionName = new DataColumn("XmlSchemaCollectionName", typeof(System.String)); + DataColumn XmlSchemaCollectionDatabase = new DataColumn("XmlSchemaCollectionDatabase", typeof(string)); + DataColumn XmlSchemaCollectionOwningSchema = new DataColumn("XmlSchemaCollectionOwningSchema", typeof(string)); + DataColumn XmlSchemaCollectionName = new DataColumn("XmlSchemaCollectionName", typeof(string)); // SparseColumnSet - DataColumn IsColumnSet = new DataColumn("IsColumnSet", typeof(System.Boolean)); + DataColumn IsColumnSet = new DataColumn("IsColumnSet", typeof(bool)); Ordinal.DefaultValue = 0; IsLong.DefaultValue = false; @@ -675,7 +655,7 @@ public override DataTable GetSchemaTable() // meta data values in SmiMetaData, however, it caused the // server suites to fall over dead. Rather than attempt to // bake it into the server, I'm fixing it up in the client. - byte precision = 0xff; // default for everything, except certain numeric types. + byte precision; // default for everything, except certain numeric types. // TODO: Consider moving this into SmiMetaData itself... switch (colMetaData.SqlDbType) @@ -714,8 +694,7 @@ public override DataTable GetSchemaTable() } else { - schemaRow[Scale] = MetaType.GetMetaTypeFromSqlDbType( - colMetaData.SqlDbType, colMetaData.IsMultiValued).Scale; + schemaRow[Scale] = MetaType.GetMetaTypeFromSqlDbType(colMetaData.SqlDbType, colMetaData.IsMultiValued).Scale; } schemaRow[AllowDBNull] = colMetaData.AllowsDBNull; @@ -825,116 +804,116 @@ public override DataTable GetSchemaTable() // public override SqlBinary GetSqlBinary(int ordinal) { - EnsureCanGetCol("GetSqlBinary", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlBinary(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlBoolean GetSqlBoolean(int ordinal) { - EnsureCanGetCol("GetSqlBoolean", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlBoolean(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlByte GetSqlByte(int ordinal) { - EnsureCanGetCol("GetSqlByte", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlByte(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlInt16 GetSqlInt16(int ordinal) { - EnsureCanGetCol("GetSqlInt16", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlInt16(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlInt32 GetSqlInt32(int ordinal) { - EnsureCanGetCol("GetSqlInt32", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlInt32(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlInt64 GetSqlInt64(int ordinal) { - EnsureCanGetCol("GetSqlInt64", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlInt64(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlSingle GetSqlSingle(int ordinal) { - EnsureCanGetCol("GetSqlSingle", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlSingle(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlDouble GetSqlDouble(int ordinal) { - EnsureCanGetCol("GetSqlDouble", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlDouble(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlMoney GetSqlMoney(int ordinal) { - EnsureCanGetCol("GetSqlMoney", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlMoney(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlDateTime GetSqlDateTime(int ordinal) { - EnsureCanGetCol("GetSqlDateTime", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlDateTime(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlDecimal GetSqlDecimal(int ordinal) { - EnsureCanGetCol("GetSqlDecimal", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlDecimal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlString GetSqlString(int ordinal) { - EnsureCanGetCol("GetSqlString", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlString(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlGuid GetSqlGuid(int ordinal) { - EnsureCanGetCol("GetSqlGuid", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlGuid(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]); } public override SqlChars GetSqlChars(int ordinal) { - EnsureCanGetCol("GetSqlChars", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlChars(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext); } public override SqlBytes GetSqlBytes(int ordinal) { - EnsureCanGetCol("GetSqlBytes", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlBytes(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext); } public override SqlXml GetSqlXml(int ordinal) { - EnsureCanGetCol("GetSqlXml", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetSqlXml(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext); } public override TimeSpan GetTimeSpan(int ordinal) { - EnsureCanGetCol("GetTimeSpan", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetTimeSpan(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.Is2008OrNewer); } public override DateTimeOffset GetDateTimeOffset(int ordinal) { - EnsureCanGetCol("GetDateTimeOffset", ordinal); + EnsureCanGetCol(ordinal); return ValueUtilsSmi.GetDateTimeOffset(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.Is2008OrNewer); } public override object GetSqlValue(int ordinal) { - EnsureCanGetCol("GetSqlValue", ordinal); + EnsureCanGetCol(ordinal); SmiMetaData metaData = _currentMetaData[ordinal]; if (_currentConnection.Is2008OrNewer) @@ -947,11 +926,11 @@ public override object GetSqlValue(int ordinal) public override int GetSqlValues(object[] values) { - EnsureCanGetCol("GetSqlValues", 0); + EnsureCanGetCol(0); - if (null == values) + if (values == null) { - throw ADP.ArgumentNull("values"); + throw ADP.ArgumentNull(nameof(values)); } int copyLength = (values.Length < _visibleColumnCount) ? values.Length : _visibleColumnCount; @@ -976,7 +955,7 @@ public override bool HasRows // public override Stream GetStream(int ordinal) { - EnsureCanGetCol("GetStream", ordinal); + EnsureCanGetCol(ordinal); SmiQueryMetaData metaData = _currentMetaData[ordinal]; @@ -998,7 +977,7 @@ public override Stream GetStream(int ordinal) public override TextReader GetTextReader(int ordinal) { - EnsureCanGetCol("GetTextReader", ordinal); + EnsureCanGetCol(ordinal); SmiQueryMetaData metaData = _currentMetaData[ordinal]; @@ -1022,13 +1001,13 @@ public override XmlReader GetXmlReader(int ordinal) { // NOTE: sql_variant can not contain a XML data type: http://msdn.microsoft.com/en-us/library/ms173829.aspx - EnsureCanGetCol("GetXmlReader", ordinal); + EnsureCanGetCol(ordinal); if (_currentMetaData[ordinal].SqlDbType != SqlDbType.Xml) { throw ADP.InvalidCast(); } - Stream stream = null; + Stream stream; if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && (!ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal))) { if (HasActiveStreamOrTextReaderOnColumn(ordinal)) @@ -1061,12 +1040,8 @@ internal enum PositionState AfterRows, // After all rows in current resultset AfterResults // After all resultsets in request } - private PositionState _currentPosition; // Where is the reader relative to incoming results? - - // - // Fields - // + private PositionState _currentPosition; // Where is the reader relative to incoming results? private bool _isOpen; // Is the reader open? private SmiQueryMetaData[] _currentMetaData; // Metadata for current resultset private int[] _indexMap; // map of indices for visible column @@ -1083,11 +1058,6 @@ internal enum PositionState private SqlSequentialStreamSmi _currentStream; // The stream on the current column (if any) private SqlSequentialTextReaderSmi _currentTextReader; // The text reader on the current column (if any) - // - // Internal methods for use by other classes in project - // - // Constructor - // // Assumes that if there were any results, the first chunk of them are in the data stream // (up to the first actual row or the end of the resultsets). unsafe internal SqlDataReaderSmi( @@ -1113,7 +1083,7 @@ SmiRequestExecutor requestExecutor internal override SmiExtendedMetaData[] GetInternalSmiMetaData() { - if (null == _currentMetaData || _visibleColumnCount == this.InternalFieldCount) + if (_currentMetaData == null || _visibleColumnCount == InternalFieldCount) { return _currentMetaData; } @@ -1147,14 +1117,9 @@ internal override SmiExtendedMetaData[] GetInternalSmiMetaData() internal override int GetLocaleId(int ordinal) { - EnsureCanGetMetaData("GetLocaleId"); + EnsureCanGetMetaData(); return (int)_currentMetaData[ordinal].LocaleId; } - - // - // Private implementation methods - // - private int InternalFieldCount { get @@ -1171,25 +1136,23 @@ private int InternalFieldCount } // Have we cleaned up internal resources? - private bool IsReallyClosed() - { - return !_isOpen; - } + private bool IsReallyClosed() => !_isOpen; // Central checkpoint for closed recordset. // Any code that requires an open recordset should call this method first! // Especially any code that accesses unmanaged memory structures whose lifetime // matches the lifetime of the unmanaged recordset. - internal void ThrowIfClosed(string operationName) + internal void ThrowIfClosed([CallerMemberName] string operationName = null) { if (IsClosed) + { throw ADP.DataReaderClosed(operationName); + } } // Central checkpoint to ensure the requested column can be accessed. // Calling this function serves to notify that it has been accessed by the user. - [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // for future compatibility - private void EnsureCanGetCol(string operationName, int ordinal) + private void EnsureCanGetCol(int ordinal, [CallerMemberName] string operationName = null) { EnsureOnRow(operationName); } @@ -1203,7 +1166,7 @@ internal void EnsureOnRow(string operationName) } } - internal void EnsureCanGetMetaData(string operationName) + internal void EnsureCanGetMetaData([CallerMemberName] string operationName = null) { ThrowIfClosed(operationName); if (FNotInResults()) @@ -1251,8 +1214,8 @@ private bool HasActiveStreamOrTextReaderOnColumn(int columnIndex) { bool active = false; - active |= ((_currentStream != null) && (_currentStream.ColumnIndex == columnIndex)); - active |= ((_currentTextReader != null) && (_currentTextReader.ColumnIndex == columnIndex)); + active |= (_currentStream != null) && (_currentStream.ColumnIndex == columnIndex); + active |= (_currentTextReader != null) && (_currentTextReader.ColumnIndex == columnIndex); return active; } @@ -1305,71 +1268,71 @@ private void BatchCompleted() private sealed class ReaderEventSink : SmiEventSink_Default { - private readonly SqlDataReaderSmi reader; + private readonly SqlDataReaderSmi _reader; internal ReaderEventSink(SqlDataReaderSmi reader, SmiEventSink parent) : base(parent) { - this.reader = reader; + _reader = reader; } internal override void MetaDataAvailable(SmiQueryMetaData[] md, bool nextEventIsRow) { - var mdLength = (null != md) ? md.Length : -1; - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, md.Length={1} nextEventIsRow={2}.", reader.ObjectID, mdLength, nextEventIsRow); + var mdLength = (md != null) ? md.Length : -1; + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, md.Length={1} nextEventIsRow={2}.", _reader.ObjectID, mdLength, nextEventIsRow); if (SqlClientEventSource.Log.IsAdvancedTraceOn()) { - if (null != md) + if (md != null) { for (int i = 0; i < md.Length; i++) { - SqlClientEventSource.Log.TraceEvent(" {0}, metaData[{1}] is {2}{3}", reader.ObjectID, i, md[i].GetType(), md[i].TraceString()); + SqlClientEventSource.Log.TraceEvent(" {0}, metaData[{1}] is {2}{3}", _reader.ObjectID, i, md[i].GetType(), md[i].TraceString()); } } } - this.reader.MetaDataAvailable(md, nextEventIsRow); + _reader.MetaDataAvailable(md, nextEventIsRow); } // Obsolete V2- method internal override void RowAvailable(ITypedGetters row) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (v2).", reader.ObjectID); - this.reader.RowAvailable(row); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (v2).", _reader.ObjectID); + _reader.RowAvailable(row); } internal override void RowAvailable(ITypedGettersV3 row) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (ITypedGettersV3).", reader.ObjectID); - this.reader.RowAvailable(row); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (ITypedGettersV3).", _reader.ObjectID); + _reader.RowAvailable(row); } internal override void RowAvailable(SmiTypedGetterSetter rowData) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (SmiTypedGetterSetter).", reader.ObjectID); - this.reader.RowAvailable(rowData); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} (SmiTypedGetterSetter).", _reader.ObjectID); + _reader.RowAvailable(rowData); } internal override void StatementCompleted(int recordsAffected) { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} recordsAffected= {1}.", reader.ObjectID, recordsAffected); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} recordsAffected= {1}.", _reader.ObjectID, recordsAffected); // devnote: relies on SmiEventSink_Default to pass event to parent // Both command and reader care about StatementCompleted, but for different reasons. base.StatementCompleted(recordsAffected); - this.reader.StatementCompleted(); + _reader.StatementCompleted(); } internal override void BatchCompleted() { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}.", reader.ObjectID); + SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}.", _reader.ObjectID); // devnote: relies on SmiEventSink_Default to pass event to parent // parent's callback *MUST* come before reader's BatchCompleted, since // reader will close the event stream during this call, and parent wants // to extract parameter values before that happens. base.BatchCompleted(); - this.reader.BatchCompleted(); + _reader.BatchCompleted(); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs index a72605e86e..bf7f86c3a7 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDelegatedTransaction.cs @@ -585,7 +585,7 @@ private void ValidateActiveOnConnection(SqlInternalConnection connection) private Guid GetGlobalTxnIdentifierFromToken() { byte[] txnGuid = new byte[16]; - Array.Copy(_connection.PromotedDTCToken, _globalTransactionsTokenVersionSizeInBytes /* Skip the version */, txnGuid, 0, txnGuid.Length); + Buffer.BlockCopy(_connection.PromotedDTCToken, _globalTransactionsTokenVersionSizeInBytes /* Skip the version */, txnGuid, 0, txnGuid.Length); return new Guid(txnGuid); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlError.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlError.cs deleted file mode 100644 index 1a50bbe2d5..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlError.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Data.SqlClient -{ - /// - [Serializable] - public sealed class SqlError - { - // bug fix - MDAC 48965 - missing source of exception - private readonly string _source = TdsEnums.SQL_PROVIDER_NAME; - private readonly int _number; - private readonly byte _state; - private readonly byte _errorClass; - [System.Runtime.Serialization.OptionalField(VersionAdded = 2)] - private readonly string _server; - private readonly string _message; - private readonly string _procedure; - private readonly int _lineNumber; - [System.Runtime.Serialization.OptionalField(VersionAdded = 4)] - private readonly int _win32ErrorCode; - - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, uint win32ErrorCode) - : this(infoNumber, errorState, errorClass, server, errorMessage, procedure, lineNumber) - { - _win32ErrorCode = (int)win32ErrorCode; - } - - internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber) - { - _number = infoNumber; - _state = errorState; - _errorClass = errorClass; - _server = server; - _message = errorMessage; - _procedure = procedure; - _lineNumber = lineNumber; - if (errorClass != 0) - { - SqlClientEventSource.Log.TryTraceEvent(" infoNumber={0}, errorState={1}, errorClass={2}, errorMessage='{3}', procedure='{4}', lineNumber={5}", infoNumber, (int)errorState, (int)errorClass, errorMessage, procedure, (int)lineNumber); - } - _win32ErrorCode = 0; - } - - /// - // bug fix - MDAC #49280 - SqlError does not implement ToString(); - // I did not include an exception stack because the correct exception stack is only available - // on SqlException, and to obtain that the SqlError would have to have backpointers all the - // way back to SqlException. If the user needs a call stack, they can obtain it on SqlException. - public override string ToString() - { - //return GetType().ToString() + ": " + message; - return typeof(SqlError).ToString() + ": " + _message; // since this is sealed so we can change GetType to typeof - } - - /// - // bug fix - MDAC #48965 - missing source of exception - public string Source - { - get { return _source; } - } - - /// - public int Number - { - get { return _number; } - } - - /// - public byte State - { - get { return _state; } - } - - /// - public byte Class - { - get { return _errorClass; } - } - - /// - public string Server - { - get { return _server; } - } - - /// - public string Message - { - get { return _message; } - } - - /// - public string Procedure - { - get { return _procedure; } - } - - /// - public int LineNumber - { - get { return _lineNumber; } - } - - internal int Win32ErrorCode - { - get { return _win32ErrorCode; } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs index 71586f1adf..aa78a60fc8 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnection.cs @@ -475,7 +475,7 @@ private void EnlistNonNull(SysTx.Transaction tx) // NOTE: Global Transactions is an Azure SQL DB only // feature where the Transaction Manager (TM) is not // MS-DTC. Sys.Tx added APIs to support Non MS-DTC - // promoter types/TM in .NET 4.6.1. Following directions + // promoter types/TM in .NET 4.6.2. Following directions // from .NETFX shiproom, to avoid a "hard-dependency" // (compile time) on Sys.Tx, we use reflection to invoke // the new APIs. Further, the _isGlobalTransaction flag diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs index 616c44a3f4..ee8d211e7b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlInternalConnectionTds.cs @@ -105,6 +105,8 @@ public void AssertUnrecoverableStateCountIsCorrect() sealed internal class SqlInternalConnectionTds : SqlInternalConnection, IDisposable { + // https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/retry-after#simple-retry-for-errors-with-http-error-codes-500-600 + internal const int MsalHttpRetryStatusCode = 429; // Connection re-route limit internal const int _maxNumberOfRedirectRoute = 10; @@ -989,7 +991,7 @@ internal override bool IsConnectionAlive(bool throwOnException) tdsReliabilitySection.Start(); #endif //DEBUG - isAlive = _parser._physicalStateObj.IsConnectionAlive(throwOnException); + isAlive = _parser._physicalStateObj.IsConnectionAlive(throwOnException); #if DEBUG } @@ -1507,7 +1509,7 @@ private void CompleteLogin(bool enlistOK) _parser._physicalStateObj.SniContext = SniContext.Snix_Login; } - private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword) + private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword, SqlConnectionEncryptOption encrypt) { // create a new login record SqlLogin login = new SqlLogin(); @@ -1628,7 +1630,7 @@ private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, // The SQLDNSCaching feature is implicitly set requestedFeatures |= TdsEnums.FeatureExtension.SQLDNSCaching; - _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo); + _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData, _originalNetworkAddressInfo, encrypt); } private void LoginFailure() @@ -1877,7 +1879,7 @@ private void LoginNoFailover(ServerInfo serverInfo, string newPassword, SecureSt throw SQL.ROR_TimeoutAfterRoutingInfo(this); } - serverInfo = new ServerInfo(ConnectionOptions, _routingInfo, serverInfo.ResolvedServerName); + serverInfo = new ServerInfo(ConnectionOptions, _routingInfo, serverInfo.ResolvedServerName, serverInfo.ServerSPN); timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = serverInfo.UserServerName; @@ -2047,7 +2049,7 @@ TimeoutTimer timeout long timeoutUnitInterval; string protocol = ConnectionOptions.NetworkLibrary; - ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost); + ServerInfo failoverServerInfo = new ServerInfo(connectionOptions, failoverHost, connectionOptions.FailoverPartnerSPN); ResolveExtendedServerName(primaryServerInfo, !redirectedUserInstance, connectionOptions); if (null == ServerProvidedFailOverPartner) @@ -2150,7 +2152,7 @@ TimeoutTimer timeout _parser = new TdsParser(ConnectionOptions.MARS, ConnectionOptions.Asynchronous); Debug.Assert(SniContext.Undefined == Parser._physicalStateObj.SniContext, $"SniContext should be Undefined; actual Value: {Parser._physicalStateObj.SniContext}"); - currentServerInfo = new ServerInfo(ConnectionOptions, _routingInfo, currentServerInfo.ResolvedServerName); + currentServerInfo = new ServerInfo(ConnectionOptions, _routingInfo, currentServerInfo.ResolvedServerName, currentServerInfo.ServerSPN); timeoutErrorInternal.SetInternalSourceType(SqlConnectionInternalSourceType.RoutingDestination); _originalClientConnectionId = _clientConnectionId; _routingDestination = currentServerInfo.UserServerName; @@ -2296,13 +2298,9 @@ private void AttemptOneLogin(ServerInfo serverInfo, string newPassword, SecureSt this, ignoreSniOpenTimeout, timeout.LegacyTimerExpire, - ConnectionOptions.Encrypt, - ConnectionOptions.TrustServerCertificate, - ConnectionOptions.IntegratedSecurity, + ConnectionOptions, withFailover, isFirstTransparentAttempt, - ConnectionOptions.Authentication, - ConnectionOptions.Certificate, _serverCallback, _clientCallback, _originalNetworkAddressInfo != null, @@ -2312,7 +2310,7 @@ private void AttemptOneLogin(ServerInfo serverInfo, string newPassword, SecureSt timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); _parser._physicalStateObj.SniContext = SniContext.Snix_Login; - this.Login(serverInfo, timeout, newPassword, newSecurePassword); + this.Login(serverInfo, timeout, newPassword, newSecurePassword, ConnectionOptions.Encrypt); timeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth); timeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.PostLogin); @@ -2384,18 +2382,18 @@ internal bool GetSessionAndReconnectIfNeeded(SqlConnection parent, int timeout = { tdsReliabilitySection.Start(); #endif //DEBUG - Task reconnectTask = parent.ValidateAndReconnect(() => - { - ThreadHasParserLockForClose = false; - _parserLock.Release(); - releaseConnectionLock = false; - }, timeout); - if (reconnectTask != null) - { - AsyncHelper.WaitForCompletion(reconnectTask, timeout); - return true; - } - return false; + Task reconnectTask = parent.ValidateAndReconnect(() => + { + ThreadHasParserLockForClose = false; + _parserLock.Release(); + releaseConnectionLock = false; + }, timeout); + if (reconnectTask != null) + { + AsyncHelper.WaitForCompletion(reconnectTask, timeout); + return true; + } + return false; #if DEBUG } finally @@ -2457,36 +2455,36 @@ internal bool IgnoreEnvChange internal void OnEnvChange(SqlEnvChange rec) { Debug.Assert(!IgnoreEnvChange, "This function should not be called if IgnoreEnvChange is set!"); - switch (rec.type) + switch (rec._type) { case TdsEnums.ENV_DATABASE: // If connection is not open and recovery is not in progresss, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalDatabase = rec.newValue; + _originalDatabase = rec._newValue; } - CurrentDatabase = rec.newValue; + CurrentDatabase = rec._newValue; break; case TdsEnums.ENV_LANG: // If connection is not open and recovery is not in progresss, store the server value as the original. if (!_fConnectionOpen && _recoverySessionData == null) { - _originalLanguage = rec.newValue; + _originalLanguage = rec._newValue; } - _currentLanguage = rec.newValue; // TODO: finish this. + _currentLanguage = rec._newValue; // TODO: finish this. break; case TdsEnums.ENV_PACKETSIZE: - _currentPacketSize = Int32.Parse(rec.newValue, CultureInfo.InvariantCulture); + _currentPacketSize = int.Parse(rec._newValue, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COLLATION: if (_currentSessionData != null) { - _currentSessionData._collation = rec.newCollation; + _currentSessionData._collation = rec._newCollation; } break; @@ -2502,11 +2500,22 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_LOGSHIPNODE: - _currentFailoverPartner = rec.newValue; + _currentFailoverPartner = rec._newValue; break; case TdsEnums.ENV_PROMOTETRANSACTION: - PromotedDTCToken = rec.newBinValue; + byte[] dtcToken; + if (rec._newBinRented) + { + dtcToken = new byte[rec._newLength]; + Buffer.BlockCopy(rec._newBinValue, 0, dtcToken, 0, dtcToken.Length); + } + else + { + dtcToken = rec._newBinValue; + rec._newBinValue = null; + } + PromotedDTCToken = dtcToken; break; case TdsEnums.ENV_TRANSACTIONENDED: @@ -2525,16 +2534,16 @@ internal void OnEnvChange(SqlEnvChange rec) break; case TdsEnums.ENV_USERINSTANCE: - _instanceName = rec.newValue; + _instanceName = rec._newValue; break; case TdsEnums.ENV_ROUTING: SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, Received routing info", ObjectID); - if (string.IsNullOrEmpty(rec.newRoutingInfo.ServerName) || rec.newRoutingInfo.Protocol != 0 || rec.newRoutingInfo.Port == 0) + if (string.IsNullOrEmpty(rec._newRoutingInfo.ServerName) || rec._newRoutingInfo.Protocol != 0 || rec._newRoutingInfo.Port == 0) { throw SQL.ROR_InvalidRoutingInfo(this); } - _routingInfo = rec.newRoutingInfo; + _routingInfo = rec._newRoutingInfo; break; default: @@ -2859,7 +2868,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) // Deal with Msal service exceptions first, retry if 429 received. catch (MsalServiceException serviceException) { - if (429 == serviceException.StatusCode) + if (serviceException.StatusCode == MsalHttpRetryStatusCode) { RetryConditionHeaderValue retryAfter = serviceException.Headers.RetryAfter; if (retryAfter.Delta.HasValue) @@ -2878,9 +2887,15 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) } else { - break; + SqlClientEventSource.Log.TryTraceEvent(" Timeout: {0}", serviceException.ErrorCode); + throw SQL.ActiveDirectoryTokenRetrievingTimeout(Enum.GetName(typeof(SqlAuthenticationMethod), ConnectionOptions.Authentication), serviceException.ErrorCode, serviceException); } } + else + { + SqlClientEventSource.Log.TryTraceEvent(" {0}", serviceException.ErrorCode); + throw ADP.CreateSqlException(serviceException, ConnectionOptions, this, username); + } } // Deal with normal MsalExceptions. catch (MsalException msalException) @@ -2891,21 +2906,7 @@ internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { SqlClientEventSource.Log.TryTraceEvent(" {0}", msalException.ErrorCode); - // Error[0] - SqlErrorCollection sqlErs = new SqlErrorCollection(); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, StringsHelper.GetString(Strings.SQL_MSALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - - // Error[1] - string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode); - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - - // Error[2] - if (!string.IsNullOrEmpty(msalException.Message)) - { - sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, msalException.Message, ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); - } - SqlException exc = SqlException.CreateException(sqlErs, "", this); - throw exc; + throw ADP.CreateSqlException(msalException, ConnectionOptions, this, username); } SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, sleeping {1}[Milliseconds]", ObjectID, sleepInterval); @@ -3244,6 +3245,7 @@ internal sealed class ServerInfo internal string ResolvedServerName { get; private set; } // the resolved servername only internal string ResolvedDatabaseName { get; private set; } // name of target database after resolution internal string UserProtocol { get; private set; } // the user specified protocol + internal string ServerSPN { get; private set; } // the server SPN // The original user-supplied server name from the connection string. // If connection string has no Data Source, the value is set to string.Empty. @@ -3264,10 +3266,16 @@ private set internal readonly string PreRoutingServerName; // Initialize server info from connection options, - internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource) { } + internal ServerInfo(SqlConnectionString userOptions) : this(userOptions, userOptions.DataSource, userOptions.ServerSPN) { } + + // Initialize server info from connection options, but override DataSource and ServerSPN with given server name and server SPN + internal ServerInfo(SqlConnectionString userOptions, string serverName, string serverSPN) : this(userOptions, serverName) + { + ServerSPN = serverSPN; + } // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, string serverName) + private ServerInfo(SqlConnectionString userOptions, string serverName) { //----------------- // Preconditions @@ -3286,7 +3294,7 @@ internal ServerInfo(SqlConnectionString userOptions, string serverName) // Initialize server info from connection options, but override DataSource with given server name - internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName) + internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string preRoutingServerName, string serverSPN) { //----------------- // Preconditions @@ -3307,6 +3315,7 @@ internal ServerInfo(SqlConnectionString userOptions, RoutingInfo routing, string UserProtocol = TdsEnums.TCP; SetDerivedNames(UserProtocol, UserServerName); ResolvedDatabaseName = userOptions.InitialCatalog; + ServerSPN = serverSPN; } internal void SetDerivedNames(string protocol, string serverName) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs deleted file mode 100644 index 503273c317..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ /dev/null @@ -1,2396 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.Design.Serialization; -using System.Data; -using System.Data.Common; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Xml; -using Microsoft.Data.Common; -using Microsoft.Data.SqlClient.Server; - -namespace Microsoft.Data.SqlClient -{ - internal abstract class DataFeed - { - } - - internal class StreamDataFeed : DataFeed - { - internal Stream _source; - - internal StreamDataFeed(Stream source) - { - _source = source; - } - } - - internal class TextDataFeed : DataFeed - { - internal TextReader _source; - - internal TextDataFeed(TextReader source) - { - _source = source; - } - } - - internal class XmlDataFeed : DataFeed - { - internal XmlReader _source; - - internal XmlDataFeed(XmlReader source) - { - _source = source; - } - } - - /// - [TypeConverter(typeof(SqlParameter.SqlParameterConverter))] - public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable - { - internal sealed class SqlParameterConverter : ExpandableObjectConverter - { - - // converter classes should have public ctor - public SqlParameterConverter() - { - } - - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (typeof(InstanceDescriptor) == destinationType) - { - return true; - } - return base.CanConvertTo(context, destinationType); - } - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - { - if (destinationType == null) - { - throw ADP.ArgumentNull(nameof(destinationType)); - } - if ((typeof(InstanceDescriptor) == destinationType) && (value is SqlParameter)) - { - return ConvertToInstanceDescriptor(value as SqlParameter); - } - return base.ConvertTo(context, culture, value, destinationType); - } - - private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) - { - int flags = 0; // if part of the collection - the parametername can't be empty - - if (p.ShouldSerializeSqlDbType()) - { - flags |= 1; - } - if (p.ShouldSerializeSize()) - { - flags |= 2; - } - if (!string.IsNullOrEmpty(p.SourceColumn)) - { - flags |= 4; - } - if (null != p.Value) - { - flags |= 8; - } - if ( - (ParameterDirection.Input != p.Direction) || - p.IsNullable || - p.ShouldSerializePrecision() || - p.ShouldSerializeScale() || - (DataRowVersion.Current != p.SourceVersion) - ) - { - flags |= 16; // v1.0 everything - } - - if ( - p.SourceColumnNullMapping || - !string.IsNullOrEmpty(p.XmlSchemaCollectionDatabase) || - !string.IsNullOrEmpty(p.XmlSchemaCollectionOwningSchema) || - !string.IsNullOrEmpty(p.XmlSchemaCollectionName) - ) - { - flags |= 32; // v2.0 everything - } - - Type[] ctorParams; - object[] ctorValues; - switch (flags) - { - case 0: // ParameterName - case 1: // SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType }; - break; - case 2: // Size - case 3: // Size, SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size }; - break; - case 4: // SourceColumn - case 5: // SourceColumn, SqlDbType - case 6: // SourceColumn, Size - case 7: // SourceColumn, Size, SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int), typeof(string) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size, p.SourceColumn }; - break; - case 8: // Value - ctorParams = new Type[] { typeof(string), typeof(object) }; - ctorValues = new object[] { p.ParameterName, p.Value }; - break; - default: - if (0 == (32 & flags)) - { // v1.0 everything - ctorParams = new Type[] { - typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection), - typeof(bool), typeof(byte), typeof(byte), - typeof(string), typeof(DataRowVersion), - typeof(object) }; - ctorValues = new object[] { - p.ParameterName, p.SqlDbType, p.Size, p.Direction, - p.IsNullable, p.PrecisionInternal, p.ScaleInternal, - p.SourceColumn, p.SourceVersion, - p.Value }; - } - else - { // v2.0 everything - round trip all browsable properties + precision/scale - ctorParams = new Type[] { - typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection), - typeof(byte), typeof(byte), - typeof(string), typeof(DataRowVersion), typeof(bool), - typeof(object), - typeof(string), typeof(string), - typeof(string) }; - ctorValues = new object[] { - p.ParameterName, p.SqlDbType, p.Size, p.Direction, - p.PrecisionInternal, p.ScaleInternal, - p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping, - p.Value, - p.XmlSchemaCollectionDatabase, p.XmlSchemaCollectionOwningSchema, - p.XmlSchemaCollectionName}; - } - break; - } - ConstructorInfo ctor = typeof(SqlParameter).GetConstructor(ctorParams); - return new InstanceDescriptor(ctor, ctorValues); - } - } - - [Flags] - private enum SqlParameterFlags : ushort - { - None = 0, - IsNull = 1, - IsNullable = 2, - IsSqlParameterSqlType = 4, - SourceColumnNullMapping = 8, - CoercedValueIsSqlType = 16, - CoercedValueIsDataFeed = 32, - HasReceivedMetadata = 64, - ForceColumnEncryption = 128, - IsDerivedParameterTypeName = 256, - HasScale = 512, - } - - private MetaType _metaType; - private SqlCollation _collation; - private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; - private string _udtTypeName; - private string _typeName; - private Exception _udtLoadError; - private string _parameterName; - private byte _precision; - private byte _scale; - private MetaType _internalMetaType; - private SqlBuffer _sqlBufferReturnValue; - private INullable _valueAsINullable; - private int _actualSize; - private object _value; - private object _coercedValue; - private object _parent; - private ParameterDirection _direction; - private int _size; - private int _offset; - private string _sourceColumn; - private DataRowVersion _sourceVersion; - private SqlParameterFlags _flags; - - /// - public SqlParameter() : base() - { - _flags = SqlParameterFlags.IsNull; - _actualSize = -1; - _direction = ParameterDirection.Input; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - } - - /// - public SqlParameter(string parameterName, object value) : this() - { - Debug.Assert(!(value is SqlDbType), "use SqlParameter(string, SqlDbType)"); - - ParameterName = parameterName; - Value = value; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - SourceColumn = sourceColumn; - } - - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public SqlParameter( - string parameterName, - SqlDbType dbType, - int size, - ParameterDirection direction, - bool isNullable, - byte precision, - byte scale, - string sourceColumn, - DataRowVersion sourceVersion, - object value - ) - : this(parameterName, dbType, size, sourceColumn) - { - Direction = direction; - IsNullable = isNullable; - PrecisionInternal = precision; - ScaleInternal = scale; - SourceVersion = sourceVersion; - Value = value; - } - - /// - public SqlParameter( - string parameterName, - SqlDbType dbType, - int size, - ParameterDirection direction, - byte precision, - byte scale, - string sourceColumn, - DataRowVersion sourceVersion, - bool sourceColumnNullMapping, - object value, - string xmlSchemaCollectionDatabase, - string xmlSchemaCollectionOwningSchema, - string xmlSchemaCollectionName - ) - : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - Direction = direction; - PrecisionInternal = precision; - ScaleInternal = scale; - SourceColumn = sourceColumn; - SourceVersion = sourceVersion; - SourceColumnNullMapping = sourceColumnNullMapping; - Value = value; - if (!string.IsNullOrEmpty(xmlSchemaCollectionDatabase) || !string.IsNullOrEmpty(xmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(xmlSchemaCollectionName)) - { - EnsureXmlSchemaCollection(); - _xmlSchemaCollection.Database = xmlSchemaCollectionDatabase; - _xmlSchemaCollection.OwningSchema = xmlSchemaCollectionOwningSchema; - _xmlSchemaCollection.Name = xmlSchemaCollectionName; - } - } - - private SqlParameter(SqlParameter source) : this() - { - ADP.CheckArgumentNull(source, nameof(source)); - source.CloneHelper(this); - if (_value is ICloneable cloneable) - { - _value = cloneable.Clone(); - } - } - - /// - /// Get or set the encryption related metadata of this SqlParameter. - /// Should be set to a non-null value only once. - /// - internal SqlCipherMetadata CipherMetadata { get; set; } - - /// - /// Indicates if the parameter encryption metadata received by sp_describe_parameter_encryption. - /// For unencrypted parameters, the encryption metadata should still be sent (and will indicate - /// that no encryption is needed). - /// - internal bool HasReceivedMetadata - { - get => HasFlag(SqlParameterFlags.HasReceivedMetadata); - set => SetFlag(SqlParameterFlags.HasReceivedMetadata, value); - } - - /// - /// Returns the normalization rule version number as a byte - /// - internal byte NormalizationRuleVersion => CipherMetadata?.NormalizationRuleVersion ?? 0x00; - - /// - [Browsable(false)] - public SqlCompareOptions CompareInfo - { - // Bits 21 through 25 represent the CompareInfo - get - { - SqlCollation collation = _collation; - if (null != collation) - { - return collation.SqlCompareOptions; - } - return SqlCompareOptions.None; - } - set - { - SqlCollation collation = _collation; - - // Copied from SQLString.x_iValidSqlCompareOptionMask - SqlCompareOptions validSqlCompareOptionMask = - SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | - SqlCompareOptions.IgnoreNonSpace | SqlCompareOptions.IgnoreKanaType | - SqlCompareOptions.BinarySort | SqlCompareOptions.BinarySort2; - - if ((value & validSqlCompareOptionMask) != value) - { - throw ADP.ArgumentOutOfRange(nameof(CompareInfo)); - } - - if (collation == null || collation.SqlCompareOptions != value) - { - _collation = SqlCollation.FromLCIDAndSort(collation?.LCID ?? 0, value); - } - } - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionDatabase - { - get => _xmlSchemaCollection?.Database ?? string.Empty; - set => EnsureXmlSchemaCollection().Database = value; - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionOwningSchema - { - get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; - set => EnsureXmlSchemaCollection().OwningSchema = value; - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionName - { - get => _xmlSchemaCollection?.Name ?? string.Empty; - set => EnsureXmlSchemaCollection().Name = value; - } - - /// - [ - DefaultValue(false), - ResCategory("Data") - ] - public bool ForceColumnEncryption - { - get => HasFlag(SqlParameterFlags.ForceColumnEncryption); - set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); - } - - /// - public override DbType DbType - { - get => GetMetaTypeOnly().DbType; - set - { - MetaType metatype = _metaType; - if ((null == metatype) || (metatype.DbType != value)) - { - PropertyTypeChanging(); - _metaType = MetaType.GetMetaTypeFromDbType(value); - } - } - } - - /// - public override void ResetDbType() => ResetSqlDbType(); - - /// - [ResCategory("Data")] - public override string ParameterName - { - get => _parameterName ?? string.Empty; - set - { - if ( - string.IsNullOrEmpty(value) || - (value.Length < TdsEnums.MAX_PARAMETER_NAME_LENGTH) || - ( - (value[0] == '@') && - (value.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH) - ) - ) - { - if (_parameterName != value) - { - PropertyChanging(); - _parameterName = value; - } - } - else - { - throw SQL.InvalidParameterNameLength(value); - } - } - } - - /// - [Browsable(false)] - public int LocaleId - { - // Lowest 20 bits represent LocaleId - get - { - SqlCollation collation = _collation; - if (null != collation) - { - return collation.LCID; - } - return 0; - } - set - { - SqlCollation collation = _collation; - - if (value != (SqlCollation.MaskLcid & value)) - { - throw ADP.ArgumentOutOfRange(nameof(LocaleId)); - } - - if (collation == null || collation.LCID != value) - { - _collation = SqlCollation.FromLCIDAndSort(value, collation?.SqlCompareOptions ?? SqlCompareOptions.None); - } - } - } - - /// - [ - DefaultValue((byte)0), - ResCategory(StringsHelper.ResourceNames.DataCategory_Data) - ] - public new byte Precision - { - get => PrecisionInternal; - set => PrecisionInternal = value; - } - - private bool ShouldSerializePrecision() => _precision != 0; - - /// - [ - DefaultValue((byte)0), - ResCategory(StringsHelper.ResourceNames.DataCategory_Data) - ] - public new byte Scale - { - get => ScaleInternal; - set => ScaleInternal = value; - } - - internal byte ScaleInternal - { - get - { - byte scale = _scale; - SqlDbType dbtype = GetMetaSqlDbTypeOnly(); - if ((scale == 0) && (dbtype == SqlDbType.Decimal)) - { - scale = ValueScale(SqlValue); - } - return scale; - } - set - { - if (_scale != value || !HasFlag(SqlParameterFlags.HasScale)) - { - PropertyChanging(); - _scale = value; - SetFlag(SqlParameterFlags.HasScale, true); - _actualSize = -1; // Invalidate actual size such that it is re-calculated - } - } - } - - private bool ShouldSerializeScale() => _scale != 0; // V1.0 compat, ignore _hasScale - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - DbProviderSpecificTypeProperty(true) - ] - public SqlDbType SqlDbType - { - get => GetMetaTypeOnly().SqlDbType; - set - { - MetaType metatype = _metaType; - // HACK!!! - // We didn't want to expose SmallVarBinary on SqlDbType so we - // stuck it at the end of SqlDbType in v1.0, except that now - // we have new data types after that and it's smack dab in the - // middle of the valid range. To prevent folks from setting - // this invalid value we have to have this code here until we - // can take the time to fix it later. - if (TdsEnums.SmallVarBinary == value) - { - throw SQL.InvalidSqlDbType(value); - } - if ((null == metatype) || (metatype.SqlDbType != value)) - { - PropertyTypeChanging(); - _metaType = MetaType.GetMetaTypeFromSqlDbType(value, value == SqlDbType.Structured); - } - } - } - - private bool ShouldSerializeSqlDbType() => _metaType != null; - - /// - public void ResetSqlDbType() - { - if (_metaType != null) - { - PropertyTypeChanging(); - _metaType = null; - } - } - - /// - [ - Browsable(false), - DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), - ] - public object SqlValue - { - get - { - if (_udtLoadError != null) - { - throw _udtLoadError; - } - - if (_value != null) - { - if (_value == DBNull.Value) - { - return MetaType.GetNullSqlValue(GetMetaTypeOnly().SqlType); - } - if (_value is INullable) - { - return _value; - } - - // For Date and DateTime2, return the CLR object directly without converting it to a SqlValue - // GetMetaTypeOnly() will convert _value to a string in the case of char or char[], so only check - // the SqlDbType for DateTime. This is the only case when we might return the CLR value directly. - if (_value is DateTime) - { - SqlDbType sqlDbType = GetMetaTypeOnly().SqlDbType; - if (sqlDbType == SqlDbType.Date || sqlDbType == SqlDbType.DateTime2) - { - return _value; - } - } - - return (MetaType.GetSqlValueFromComVariant(_value)); - } - else if (_sqlBufferReturnValue != null) - { - return _sqlBufferReturnValue.SqlValue; - } - return null; - } - set - { - Value = value; - } - } - - /// - [ - Browsable(false), - EditorBrowsable(EditorBrowsableState.Advanced) - ] - public string UdtTypeName - { - get => _udtTypeName ?? string.Empty; - set => _udtTypeName = value; - } - - /// - [ - Browsable(false), - EditorBrowsable(EditorBrowsableState.Advanced) - ] - public string TypeName - { - get => _typeName ?? string.Empty; - set - { - _typeName = value; - IsDerivedParameterTypeName = false; - } - } - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - TypeConverter(typeof(StringConverter)), - ] - public override object Value - { - get - { - if (_udtLoadError != null) - { - throw _udtLoadError; - } - - if (_value != null) - { - return _value; - } - else if (_sqlBufferReturnValue != null) - { - if (ParameterIsSqlType) - { - return _sqlBufferReturnValue.SqlValue; - } - return _sqlBufferReturnValue.Value; - } - return null; - } - set - { - _value = value; - _sqlBufferReturnValue = null; - _coercedValue = null; - _valueAsINullable = _value as INullable; - SetFlag(SqlParameterFlags.IsSqlParameterSqlType, _valueAsINullable != null); - SetFlag(SqlParameterFlags.IsNull, (null == _value) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); - _udtLoadError = null; - _actualSize = -1; - } - } - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - ] - public override ParameterDirection Direction - { - get => _direction; - set - { - if (_direction != value) - { - switch (value) - { - case ParameterDirection.Input: - case ParameterDirection.Output: - case ParameterDirection.InputOutput: - case ParameterDirection.ReturnValue: - PropertyChanging(); - _direction = value; - break; - default: - throw ADP.InvalidParameterDirection(value); - } - } - } - } - - /// - public override bool IsNullable - { - get => HasFlag(SqlParameterFlags.IsNullable); - set => SetFlag(SqlParameterFlags.IsNullable, value); - } - - /// - public int Offset - { - get => _offset; - set - { - if (value < 0) - { - throw ADP.InvalidOffsetValue(value); - } - _offset = value; - } - } - - /// - [ResCategory("Data")] - public override int Size - { - get - { - int size = _size; - if (size == 0) - { - size = ValueSize(Value); - } - return size; - } - set - { - if (value != _size) - { - if (value < -1) - { - throw ADP.InvalidSizeValue(value); - } - PropertyChanging(); - _size = value; - } - } - } - - private void ResetSize() - { - if (_size != 0) - { - PropertyChanging(); - _size = 0; - } - } - - private bool ShouldSerializeSize() => _size != 0; - - /// - [ResCategory("Update")] - public override string SourceColumn - { - get => _sourceColumn ?? string.Empty; - set => _sourceColumn = value; - } - - /// - public override bool SourceColumnNullMapping - { - get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); - set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); - } - - /// - public override string ToString() => ParameterName; - - /// - [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] - public override DataRowVersion SourceVersion - { - get - { - DataRowVersion sourceVersion = _sourceVersion; - return (sourceVersion != 0) ? sourceVersion : DataRowVersion.Current; - } - set - { - switch (value) - { - case DataRowVersion.Original: - case DataRowVersion.Current: - case DataRowVersion.Proposed: - case DataRowVersion.Default: - _sourceVersion = value; - break; - default: - throw ADP.InvalidDataRowVersion(value); - } - } - } - - - /// - object ICloneable.Clone() => new SqlParameter(this); - - - private object CoercedValue - { - get => _coercedValue; - set => _coercedValue = value; - } - - internal bool CoercedValueIsDataFeed - { - get - { - if (null == _coercedValue) - { - GetCoercedValue(); - } - AssertCachedPropertiesAreValid(); - return HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); - } - } - - internal bool CoercedValueIsSqlType - { - get - { - if (_coercedValue == null) - { - GetCoercedValue(); - } - AssertCachedPropertiesAreValid(); - return HasFlag(SqlParameterFlags.CoercedValueIsSqlType); - } - } - - // - // currently the user can't set this value. it gets set by the returnvalue from tds - // - internal SqlCollation Collation - { - get => _collation; - set => _collation = value; - } - - private bool HasFlag(SqlParameterFlags flag) - { - return (_flags & flag) != 0; - } - - internal bool IsNull - { - get - { - // NOTE: Udts can change their value any time - if (_internalMetaType.SqlDbType == SqlDbType.Udt) - { - SetFlag(SqlParameterFlags.IsNull, (_value == null) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); - } - return HasFlag(SqlParameterFlags.IsNull); - } - } - - internal MetaType InternalMetaType - { - get - { - Debug.Assert(null != _internalMetaType, "null InternalMetaType"); - return _internalMetaType; - } - set => _internalMetaType = value; - } - - internal byte PrecisionInternal - { - get - { - byte precision = _precision; - SqlDbType dbtype = GetMetaSqlDbTypeOnly(); - if ((0 == precision) && (SqlDbType.Decimal == dbtype)) - { - precision = ValuePrecision(SqlValue); - } - return precision; - } - set - { - SqlDbType sqlDbType = SqlDbType; - if (sqlDbType == SqlDbType.Decimal && value > TdsEnums.MAX_NUMERIC_PRECISION) - { - throw SQL.PrecisionValueOutOfRange(value); - } - if (_precision != value) - { - PropertyChanging(); - _precision = value; - } - } - } - - internal bool ParameterIsSqlType - { - get => HasFlag(SqlParameterFlags.IsSqlParameterSqlType); - set => SetFlag(SqlParameterFlags.IsSqlParameterSqlType, value); - } - - internal string ParameterNameFixed - { - get - { - string parameterName = ParameterName; - if ((parameterName.Length > 0) && (parameterName[0] != '@')) - { - parameterName = "@" + parameterName; - } - Debug.Assert(parameterName.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH, "parameter name too long"); - return parameterName; - } - } - - internal bool SizeInferred => 0 == _size; - - internal INullable ValueAsINullable => _valueAsINullable; - - internal bool IsDerivedParameterTypeName - { - get => HasFlag(SqlParameterFlags.IsDerivedParameterTypeName); - set => SetFlag(SqlParameterFlags.IsDerivedParameterTypeName, value); - } - - private void CloneHelper(SqlParameter destination) - { - // NOTE: _parent is not cloned - destination._value = _value; - destination._direction = _direction; - destination._size = _size; - destination._offset = _offset; - destination._sourceColumn = _sourceColumn; - destination._sourceVersion = _sourceVersion; - destination._flags = _flags & ( - SqlParameterFlags.SourceColumnNullMapping | - SqlParameterFlags.IsNull | - SqlParameterFlags.IsNullable | - SqlParameterFlags.IsSqlParameterSqlType | - SqlParameterFlags.CoercedValueIsDataFeed | - SqlParameterFlags.CoercedValueIsSqlType | - SqlParameterFlags.ForceColumnEncryption | - SqlParameterFlags.IsDerivedParameterTypeName - // HasScale and HasReceivedMetadata deliberately omitted - ); - destination._metaType = _metaType; - destination._collation = _collation; - if (_xmlSchemaCollection != null) - { - destination.EnsureXmlSchemaCollection().CopyFrom(_xmlSchemaCollection); - } - destination._udtTypeName = _udtTypeName; - destination._typeName = _typeName; - destination._udtLoadError = _udtLoadError; - destination._parameterName = _parameterName; - destination._precision = _precision; - destination._scale = _scale; - destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._internalMetaType = _internalMetaType; - destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem - destination._valueAsINullable = _valueAsINullable; - destination._actualSize = _actualSize; - } - - internal void CopyTo(SqlParameter destination) - { - ADP.CheckArgumentNull(destination, nameof(destination)); - CloneHelper(destination); - } - - internal object CompareExchangeParent(object value, object comparand) - { - // the interlock guarantees same parameter won't belong to multiple collections - // at the same time, but to actually occur the user must really try - // since we never declared thread safety, we don't care at this time - //return System.Threading.Interlocked.CompareExchange(ref _parent, value, comparand); - object parent = _parent; - if (comparand == parent) - { - _parent = value; - } - return parent; - } - - private SqlMetaDataXmlSchemaCollection EnsureXmlSchemaCollection() - { - if (_xmlSchemaCollection is null) - { - _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); - } - return _xmlSchemaCollection; - } - - internal void FixStreamDataForNonPLP() - { - object value = GetCoercedValue(); - AssertCachedPropertiesAreValid(); - if (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) - { - return; - } - - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); - - if (value is TextDataFeed textFeed) - { - if (Size > 0) - { - char[] buffer = new char[Size]; - int nRead = textFeed._source.ReadBlock(buffer, 0, Size); - CoercedValue = new string(buffer, 0, nRead); - } - else - { - CoercedValue = textFeed._source.ReadToEnd(); - } - return; - } - - if (value is StreamDataFeed streamFeed) - { - if (Size > 0) - { - byte[] buffer = new byte[Size]; - int totalRead = 0; - Stream sourceStream = streamFeed._source; - while (totalRead < Size) - { - int nRead = sourceStream.Read(buffer, totalRead, Size - totalRead); - if (nRead == 0) - { - break; - } - totalRead += nRead; - } - if (totalRead < Size) - { - Array.Resize(ref buffer, totalRead); - } - CoercedValue = buffer; - } - else - { - MemoryStream ms = new MemoryStream(); - streamFeed._source.CopyTo(ms); - CoercedValue = ms.ToArray(); - } - return; - } - - if (value is XmlDataFeed xmlFeed) - { - CoercedValue = MetaType.GetStringFromXml(xmlFeed._source); - return; - } - - // We should have returned before reaching here - Debug.Fail("_coercedValueIsDataFeed was true, but the value was not a known DataFeed type"); - } - - private void GetActualFieldsAndProperties(out List fields, out SmiMetaDataPropertyCollection props, out ParameterPeekAheadValue peekAhead) - { - fields = null; - props = null; - peekAhead = null; - - object value = GetCoercedValue(); - if (value is DataTable dt) - { - if (dt.Columns.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - fields = new List(dt.Columns.Count); - bool[] keyCols = new bool[dt.Columns.Count]; - bool hasKey = false; - - // set up primary key as unique key list - // do this prior to general metadata loop to favor the primary key - if (null != dt.PrimaryKey && 0 < dt.PrimaryKey.Length) - { - foreach (DataColumn col in dt.PrimaryKey) - { - keyCols[col.Ordinal] = true; - hasKey = true; - } - } - - for (int i = 0; i < dt.Columns.Count; i++) - { - fields.Add(MetaDataUtilsSmi.SmiMetaDataFromDataColumn(dt.Columns[i], dt)); - - // DataColumn uniqueness is only for a single column, so don't add - // more than one. (keyCols.Count first for assumed minimal perf benefit) - if (!hasKey && dt.Columns[i].Unique) - { - keyCols[i] = true; - hasKey = true; - } - } - - // Add unique key property, if any found. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - else if (value is SqlDataReader sqlReader) - { - fields = new List(sqlReader.GetInternalSmiMetaData()); - if (fields.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - - bool[] keyCols = new bool[fields.Count]; - bool hasKey = false; - for (int i = 0; i < fields.Count; i++) - { - if (fields[i] is SmiQueryMetaData qmd && !qmd.IsKey.IsNull && qmd.IsKey.Value) - { - keyCols[i] = true; - hasKey = true; - } - } - - // Add unique key property, if any found. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - else if (value is IEnumerable enumerable) - { - // must grab the first record of the enumerator to get the metadata - IEnumerator enumerator = enumerable.GetEnumerator(); - SqlDataRecord firstRecord = null; - try - { - // no need for fields if there's no rows or no columns -- we'll be sending a null instance anyway. - if (enumerator.MoveNext()) - { - firstRecord = enumerator.Current; - int fieldCount = firstRecord.FieldCount; - if (0 < fieldCount) - { - // It's valid! Grab those fields. - bool[] keyCols = new bool[fieldCount]; - bool[] defaultFields = new bool[fieldCount]; - bool[] sortOrdinalSpecified = new bool[fieldCount]; - int maxSortOrdinal = -1; // largest sort ordinal seen, used to optimize locating holes in the list - bool hasKey = false; - bool hasDefault = false; - int sortCount = 0; - SmiOrderProperty.SmiColumnOrder[] sort = new SmiOrderProperty.SmiColumnOrder[fieldCount]; - fields = new List(fieldCount); - for (int i = 0; i < fieldCount; i++) - { - SqlMetaData colMeta = firstRecord.GetSqlMetaData(i); - fields.Add(MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(colMeta)); - if (colMeta.IsUniqueKey) - { - keyCols[i] = true; - hasKey = true; - } - - if (colMeta.UseServerDefault) - { - defaultFields[i] = true; - hasDefault = true; - } - - PropertyInfo serverTypeNameProperty = colMeta.GetType().GetProperty("SortOrder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = serverTypeNameProperty.GetGetMethod(nonPublic: true); - SortOrder sortOrder = (SortOrder)getter.Invoke(colMeta, null); - - sort[i]._order = sortOrder; - if (SortOrder.Unspecified != sortOrder) - { - // SqlMetaData takes care of checking for negative sort ordinals with specified sort order - - // bail early if there's no way sort order could be monotonically increasing - if (fieldCount <= colMeta.SortOrdinal) - { - throw SQL.SortOrdinalGreaterThanFieldCount(i, colMeta.SortOrdinal); - } - - // Check to make sure we haven't seen this ordinal before - if (sortOrdinalSpecified[colMeta.SortOrdinal]) - { - throw SQL.DuplicateSortOrdinal(colMeta.SortOrdinal); - } - - sort[i]._sortOrdinal = colMeta.SortOrdinal; - sortOrdinalSpecified[colMeta.SortOrdinal] = true; - if (colMeta.SortOrdinal > maxSortOrdinal) - { - maxSortOrdinal = colMeta.SortOrdinal; - } - sortCount++; - } - } - - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - - if (hasDefault) - { - // May have already created props list in unique key handling - if (null == props) - { - props = new SmiMetaDataPropertyCollection(); - } - - props[SmiPropertySelector.DefaultFields] = new SmiDefaultFieldsProperty(new List(defaultFields)); - } - - if (0 < sortCount) - { - // validate monotonically increasing sort order. - // Since we already checked for duplicates, we just need - // to watch for values outside of the sortCount range. - if (maxSortOrdinal >= sortCount) - { - // there is at least one hole, find the first one - int i; - for (i = 0; i < sortCount; i++) - { - if (!sortOrdinalSpecified[i]) - { - break; - } - } - Debug.Assert(i < sortCount, "SqlParameter.GetActualFieldsAndProperties: SortOrdinal hole-finding algorithm failed!"); - throw SQL.MissingSortOrdinal(i); - } - - // May have already created props list - if (null == props) - { - props = new SmiMetaDataPropertyCollection(); - } - - props[SmiPropertySelector.SortOrder] = new SmiOrderProperty( - new List(sort)); - } - - // pack it up so we don't have to rewind to send the first value - peekAhead = new ParameterPeekAheadValue() - { - Enumerator = enumerator, - FirstRecord = firstRecord - }; - - // now that it's all packaged, make sure we don't dispose it. - enumerator = null; - } - else - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - } - else - { - throw SQL.IEnumerableOfSqlDataRecordHasNoRows(); - } - } - finally - { - if (enumerator != null) - { - enumerator.Dispose(); - } - } - } - else if (value is DbDataReader dbReader) - { - DataTable schema = dbReader.GetSchemaTable(); - if (schema.Rows.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - - int fieldCount = schema.Rows.Count; - fields = new List(fieldCount); - bool[] keyCols = new bool[fieldCount]; - bool hasKey = false; - int ordinalForIsKey = schema.Columns[SchemaTableColumn.IsKey].Ordinal; - int ordinalForColumnOrdinal = schema.Columns[SchemaTableColumn.ColumnOrdinal].Ordinal; - // Extract column metadata - for (int rowOrdinal = 0; rowOrdinal < fieldCount; rowOrdinal++) - { - DataRow row = schema.Rows[rowOrdinal]; - SmiExtendedMetaData candidateMd = MetaDataUtilsSmi.SmiMetaDataFromSchemaTableRow(row); - - // Determine destination ordinal. Allow for ordinal not specified by assuming rowOrdinal *is* columnOrdinal - // in that case, but don't worry about mix-and-match of the two techniques - int columnOrdinal = rowOrdinal; - if (!row.IsNull(ordinalForColumnOrdinal)) - { - columnOrdinal = (int)row[ordinalForColumnOrdinal]; - } - - // After this point, things we are creating (keyCols, fields) should be accessed by columnOrdinal - // while the source should just be accessed via "row". - - // Watch for out-of-range ordinals - if (columnOrdinal >= fieldCount || columnOrdinal < 0) - { - throw SQL.InvalidSchemaTableOrdinals(); - } - - // extend empty space if out-of-order ordinal - while (columnOrdinal > fields.Count) - { - fields.Add(null); - } - - // Now add the candidate to the list - if (fields.Count == columnOrdinal) - { - fields.Add(candidateMd); - } - else - { - // Disallow two columns using the same ordinal (even if due to mixing null and non-null columnOrdinals) - if (fields[columnOrdinal] != null) - { - throw SQL.InvalidSchemaTableOrdinals(); - } - - // Don't use insert, since it shifts all later columns down a notch - fields[columnOrdinal] = candidateMd; - } - - // Propagate key information - if (!row.IsNull(ordinalForIsKey) && (bool)row[ordinalForIsKey]) - { - keyCols[columnOrdinal] = true; - hasKey = true; - } - } - -#if DEBUG - // Check for holes - // Above loop logic prevents holes since: - // 1) loop processes fieldcount # of columns - // 2) no ordinals outside continuous range from 0 to fieldcount - 1 are allowed - // 3) no duplicate ordinals are allowed - // But assert no holes to be sure. - foreach (SmiExtendedMetaData md in fields) - { - Debug.Assert(null != md, "Shouldn't be able to have holes, since original loop algorithm prevents such."); - } -#endif - - // Add unique key property, if any defined. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - } - - internal byte GetActualScale() - { - if (ShouldSerializeScale()) - { - return ScaleInternal; - } - - // issue: how could a user specify 0 as the actual scale? - if (GetMetaTypeOnly().IsVarTime) - { - return TdsEnums.DEFAULT_VARTIME_SCALE; - } - return ValueScale(CoercedValue); - } - - // - // always returns data in bytes - except for non-unicode chars, which will be in number of chars - // - internal int GetActualSize() - { - MetaType mt = InternalMetaType; - SqlDbType actualType = mt.SqlDbType; - // NOTE: Users can change the Udt at any time, so we may need to recalculate - if ((_actualSize == -1) || (actualType == SqlDbType.Udt)) - { - _actualSize = 0; - object val = GetCoercedValue(); - bool isSqlVariant = false; - - if (IsNull && !mt.IsVarTime) - { - return 0; - } - - // if this is a backend SQLVariant type, then infer the TDS type from the SQLVariant type - if (actualType == SqlDbType.Variant) - { - mt = MetaType.GetMetaTypeFromValue(val, streamAllowed: false); - actualType = MetaType.GetSqlDataType(mt.TDSType, 0 /*no user type*/, 0 /*non-nullable type*/).SqlDbType; - isSqlVariant = true; - } - - if (mt.IsFixed) - { - _actualSize = mt.FixedLength; - } - else - { - // @hack: until we have ForceOffset behavior we have the following semantics: - // @hack: if the user supplies a Size through the Size property or constructor, - // @hack: we only send a MAX of Size bytes over. If the actualSize is < Size, then - // @hack: we send over actualSize - int coercedSize = 0; - - // get the actual length of the data, in bytes - switch (actualType) - { - case SqlDbType.NChar: - case SqlDbType.NVarChar: - case SqlDbType.NText: - case SqlDbType.Xml: - { - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - _actualSize <<= 1; - } - break; - case SqlDbType.Char: - case SqlDbType.VarChar: - case SqlDbType.Text: - { - // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - } - break; - case SqlDbType.Binary: - case SqlDbType.VarBinary: - case SqlDbType.Image: - case SqlDbType.Timestamp: - coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - break; - case SqlDbType.Udt: - if (!IsNull) - { - //call the static function - coercedSize = AssemblyCache.GetLength(val); - } - break; - case SqlDbType.Structured: - coercedSize = -1; - break; - case SqlDbType.Time: - _actualSize = isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale()); - break; - case SqlDbType.DateTime2: - // Date in number of days (3 bytes) + time - _actualSize = 3 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale())); - break; - case SqlDbType.DateTimeOffset: - // Date in days (3 bytes) + offset in minutes (2 bytes) + time - _actualSize = 5 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale())); - break; - default: - Debug.Fail("Unknown variable length type!"); - break; - } - - // don't even send big values over to the variant - if (isSqlVariant && (coercedSize > TdsEnums.TYPE_SIZE_LIMIT)) - { - throw SQL.ParameterInvalidVariant(ParameterName); - } - } - } - - return _actualSize; - } - - internal byte GetActualPrecision() - { - return ShouldSerializePrecision() ? PrecisionInternal : ValuePrecision(CoercedValue); - } - - internal object GetCoercedValue() - { - // NOTE: User can change the Udt at any time - if ((_coercedValue == null) || (_internalMetaType.SqlDbType == SqlDbType.Udt)) - { // will also be set during parameter Validation - bool isDataFeed = Value is DataFeed; - if (IsNull || isDataFeed) - { - // No coercion is done for DataFeeds and Nulls - _coercedValue = Value; - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, _coercedValue != null && HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); // set to null for output parameters that keeps _isSqlParameterSqlType - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, isDataFeed); - _actualSize = IsNull ? 0 : -1; - } - else - { - _coercedValue = CoerceValue(Value, _internalMetaType, out bool coercedValueIsDataFeed, out bool typeChanged); - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, coercedValueIsDataFeed); - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type - _actualSize = -1; - } - } - AssertCachedPropertiesAreValid(); - return _coercedValue; - } - - internal int GetParameterSize() - { - return ShouldSerializeSize() ? Size : ValueSize(CoercedValue); - } - - /// - /// Get SMI Metadata to write out type_info stream. - /// - /// - internal SmiParameterMetaData GetMetadataForTypeInfo() - { - if (_internalMetaType == null) - { - _internalMetaType = GetMetaTypeOnly(); - } - - return MetaDataForSmi(out _); - } - - // IMPORTANT DEVNOTE: This method is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter. - // Please consider impact to that when changing this method. Refer to the callers of SqlParameter.GetMetadataForTypeInfo(). - internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhead) - { - peekAhead = null; - MetaType mt = ValidateTypeLengths(true /* 2005 or newer */ ); - long actualLen = GetActualSize(); - long maxLen = Size; - - // GetActualSize returns bytes length, but smi expects char length for - // character types, so adjust - if (!mt.IsLong) - { - if (mt.SqlDbType == SqlDbType.NChar || mt.SqlDbType == SqlDbType.NVarChar) - { - actualLen /= sizeof(char); - } - - if (actualLen > maxLen) - { - maxLen = actualLen; - } - } - - // Determine maxLength for types that ValidateTypeLengths won't figure out - if (maxLen == 0) - { - if (mt.SqlDbType == SqlDbType.Binary || mt.SqlDbType == SqlDbType.VarBinary) - { - maxLen = SmiMetaData.MaxBinaryLength; - } - else if (mt.SqlDbType == SqlDbType.Char || mt.SqlDbType == SqlDbType.VarChar) - { - maxLen = SmiMetaData.MaxANSICharacters; - } - else if (mt.SqlDbType == SqlDbType.NChar || mt.SqlDbType == SqlDbType.NVarChar) - { - maxLen = SmiMetaData.MaxUnicodeCharacters; - } - } - else if ( - (maxLen > SmiMetaData.MaxBinaryLength && (SqlDbType.Binary == mt.SqlDbType || SqlDbType.VarBinary == mt.SqlDbType)) || - (maxLen > SmiMetaData.MaxANSICharacters && (SqlDbType.Char == mt.SqlDbType || SqlDbType.VarChar == mt.SqlDbType)) || - (maxLen > SmiMetaData.MaxUnicodeCharacters && (SqlDbType.NChar == mt.SqlDbType || SqlDbType.NVarChar == mt.SqlDbType)) - ) - { - maxLen = -1; - } - - - int localeId = LocaleId; - if (localeId == 0 && mt.IsCharType) - { - if (GetCoercedValue() is SqlString sqlString && !sqlString.IsNull) - { - localeId = sqlString.LCID; - } - else - { - localeId = CultureInfo.CurrentCulture.LCID; - } - } - - SqlCompareOptions compareOpts = CompareInfo; - if (compareOpts == 0 && mt.IsCharType) - { - if (GetCoercedValue() is SqlString sqlString && !sqlString.IsNull) - { - compareOpts = sqlString.SqlCompareOptions; - } - else - { - compareOpts = SmiMetaData.GetDefaultForType(mt.SqlDbType).CompareOptions; - } - } - - string typeSpecificNamePart1 = null; - string typeSpecificNamePart2 = null; - string typeSpecificNamePart3 = null; - - if (SqlDbType.Xml == mt.SqlDbType) - { - typeSpecificNamePart1 = XmlSchemaCollectionDatabase; - typeSpecificNamePart2 = XmlSchemaCollectionOwningSchema; - typeSpecificNamePart3 = XmlSchemaCollectionName; - } - else if (SqlDbType.Udt == mt.SqlDbType || (SqlDbType.Structured == mt.SqlDbType && !string.IsNullOrEmpty(TypeName))) - { - // Split the input name. The type name is specified as single 3 part name. - // NOTE: ParseTypeName throws if format is incorrect - string[] names; - if (mt.SqlDbType == SqlDbType.Udt) - { - names = ParseTypeName(UdtTypeName, true /* is UdtTypeName */); - } - else - { - names = ParseTypeName(TypeName, false /* not UdtTypeName */); - } - - if (names.Length == 1) - { - typeSpecificNamePart3 = names[0]; - } - else if (names.Length == 2) - { - typeSpecificNamePart2 = names[0]; - typeSpecificNamePart3 = names[1]; - } - else if (names.Length == 3) - { - typeSpecificNamePart1 = names[0]; - typeSpecificNamePart2 = names[1]; - typeSpecificNamePart3 = names[2]; - } - else - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - - if ( - (!string.IsNullOrEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) || - (!string.IsNullOrEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length) || - (!string.IsNullOrEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length) - ) - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - } - - byte precision = GetActualPrecision(); - byte scale = GetActualScale(); - - // precision for decimal types may still need adjustment. - if (mt.SqlDbType == SqlDbType.Decimal) - { - if (precision == 0) - { - precision = TdsEnums.DEFAULT_NUMERIC_PRECISION; - } - } - - // Sub-field determination - List fields = null; - SmiMetaDataPropertyCollection extendedProperties = null; - if (mt.SqlDbType == SqlDbType.Structured) - { - GetActualFieldsAndProperties(out fields, out extendedProperties, out peekAhead); - } - - return new SmiParameterMetaData( - mt.SqlDbType, - maxLen, - precision, - scale, - localeId, - compareOpts, - null, // Udt type not used for parameters - SqlDbType.Structured == mt.SqlDbType, - fields, - extendedProperties, - ParameterNameFixed, - typeSpecificNamePart1, - typeSpecificNamePart2, - typeSpecificNamePart3, - Direction - ); - } - - [Conditional("DEBUG")] - internal void AssertCachedPropertiesAreValid() - { - AssertPropertiesAreValid(_coercedValue, HasFlag(SqlParameterFlags.CoercedValueIsSqlType), HasFlag(SqlParameterFlags.CoercedValueIsDataFeed), IsNull); - } - - [Conditional("DEBUG")] - internal void AssertPropertiesAreValid(object value, bool? isSqlType = null, bool? isDataFeed = null, bool? isNull = null) - { - Debug.Assert(!isSqlType.HasValue || (isSqlType.Value == (value is INullable)), "isSqlType is incorrect"); - Debug.Assert(!isDataFeed.HasValue || (isDataFeed.Value == (value is DataFeed)), "isDataFeed is incorrect"); - Debug.Assert(!isNull.HasValue || (isNull.Value == ADP.IsNull(value)), "isNull is incorrect"); - } - - private SqlDbType GetMetaSqlDbTypeOnly() - { - MetaType metaType = _metaType; - if (null == metaType) - { // infer the type from the value - metaType = MetaType.GetDefaultMetaType(); - } - return metaType.SqlDbType; - } - - // This may not be a good thing to do in case someone overloads the parameter type but I - // don't want to go from SqlDbType -> metaType -> TDSType - private MetaType GetMetaTypeOnly() - { - if (_metaType != null) - { - return _metaType; - } - if (null != _value && DBNull.Value != _value) - { - // We have a value set by the user then just use that value - // char and char[] are not directly supported so we convert those values to string - Type valueType = _value.GetType(); - if (valueType == typeof(char)) - { - _value = _value.ToString(); - valueType = typeof(string); - } - else if (valueType == typeof(char[])) - { - _value = new string((char[])_value); - valueType = typeof(string); - } - return MetaType.GetMetaTypeFromType(valueType); - } - else if (_sqlBufferReturnValue != null) - { // value came back from the server - Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); - if (valueType != null) - { - return MetaType.GetMetaTypeFromType(valueType); - } - } - return MetaType.GetDefaultMetaType(); - } - - internal void Prepare(SqlCommand cmd) - { - if (_metaType == null) - { - throw ADP.PrepareParameterType(cmd); - } - else if (!ShouldSerializeSize() && !_metaType.IsFixed) - { - throw ADP.PrepareParameterSize(cmd); - } - else if ((!ShouldSerializePrecision() && !ShouldSerializeScale()) && (_metaType.SqlDbType == SqlDbType.Decimal)) - { - throw ADP.PrepareParameterScale(cmd, SqlDbType.ToString()); - } - } - - private void PropertyChanging() - { - _internalMetaType = null; - } - - private void PropertyTypeChanging() - { - PropertyChanging(); - CoercedValue = null; - } - - internal void ResetParent() => _parent = null; - - private void SetFlag(SqlParameterFlags flag, bool value) - { - _flags = value ? _flags | flag : _flags & ~flag; - } - - internal void SetSqlBuffer(SqlBuffer buff) - { - _sqlBufferReturnValue = buff; - _value = null; - _coercedValue = null; - SetFlag(SqlParameterFlags.IsNull, _sqlBufferReturnValue.IsNull); - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, false); - _udtLoadError = null; - _actualSize = -1; - } - - internal void SetUdtLoadError(Exception e) - { - _udtLoadError = e; - } - - internal void Validate(int index, bool isCommandProc) - { - MetaType metaType = GetMetaTypeOnly(); - _internalMetaType = metaType; - - // SqlParameter does a Size Validation check and would fail if the size is 0. - // This condition filters all scenarios where we view a valid size 0. - if ( - ADP.IsDirection(this, ParameterDirection.Output) && - !ADP.IsDirection(this, ParameterDirection.ReturnValue) && - (!metaType.IsFixed) && - !ShouldSerializeSize() && - ((null == _value) || Convert.IsDBNull(_value)) && - (SqlDbType != SqlDbType.Timestamp) && - (SqlDbType != SqlDbType.Udt) && - // BUG: (VSTFDevDiv - 479609): Output parameter with size 0 throws for XML, TEXT, NTEXT, IMAGE. - // NOTE: (VSTFDevDiv - 479609): Not Fixed for TEXT, NTEXT, IMAGE as these are deprecated LOB types. - (SqlDbType != SqlDbType.Xml) && - !metaType.IsVarTime - ) - { - throw ADP.UninitializedParameterSize(index, metaType.ClassType); - } - - if (metaType.SqlDbType != SqlDbType.Udt && Direction != ParameterDirection.Output) - { - GetCoercedValue(); - } - - //check if the UdtTypeName is specified for Udt params - if (metaType.SqlDbType == SqlDbType.Udt) - { - if (string.IsNullOrEmpty(UdtTypeName)) - { - throw SQL.MustSetUdtTypeNameForUdtParams(); - } - } - else if (!string.IsNullOrEmpty(UdtTypeName)) - { - throw SQL.UnexpectedUdtTypeNameForNonUdtParams(); - } - - // Validate structured-type-specific details. - if (metaType.SqlDbType == SqlDbType.Structured) - { - if (!isCommandProc && string.IsNullOrEmpty(TypeName)) - { - throw SQL.MustSetTypeNameForParam(metaType.TypeName, ParameterName); - } - - if (Direction != ParameterDirection.Input) - { - throw SQL.UnsupportedTVPOutputParameter(Direction, ParameterName); - } - - if (GetCoercedValue() == DBNull.Value) - { - throw SQL.DBNullNotSupportedForTVPValues(ParameterName); - } - } - else if (!string.IsNullOrEmpty(TypeName)) - { - throw SQL.UnexpectedTypeNameForNonStructParams(ParameterName); - } - } - - // func will change type to that with a 4 byte length if the type has a two - // byte length and a parameter length > than that expressible in 2 bytes - internal MetaType ValidateTypeLengths(bool is2005OrNewer) - { - MetaType mt = InternalMetaType; - // Since the server will automatically reject any - // char, varchar, binary, varbinary, nchar, or nvarchar parameter that has a - // byte sizeInCharacters > 8000 bytes, we promote the parameter to image, text, or ntext. This - // allows the user to specify a parameter type using a COM+ datatype and be able to - // use that parameter against a BLOB column. - if ((mt.SqlDbType != SqlDbType.Udt) && !mt.IsFixed && !mt.IsLong) - { // if type has 2 byte length - long actualSizeInBytes = GetActualSize(); - long sizeInCharacters = Size; - - // Bug: VSTFDevDiv #636867 - // Notes: - // 'actualSizeInBytes' is the size of value passed; - // 'sizeInCharacters' is the parameter size; - // 'actualSizeInBytes' is in bytes; - // 'this.Size' is in charaters; - // 'sizeInCharacters' is in characters; - // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes; - // For Non-NCharType and for non-2005 or greater variables, size should be maintained; - // Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression; - // Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and - // 'maxSize' to 'maxSizeInBytes' - // The idea is to - // 1) revert the regression from bug 479739 - // 2) fix as many scenarios as possible including bug 636867 - // 3) cause no additional regression from 3.5 sp1 - // Keeping these goals in mind - the following are the changes we are making - - long maxSizeInBytes; - if (mt.IsNCharType && is2005OrNewer) - { - maxSizeInBytes = ((sizeInCharacters * sizeof(char)) > actualSizeInBytes) ? sizeInCharacters * sizeof(char) : actualSizeInBytes; - } - else - { - // Notes: - // Elevation from (n)(var)char (4001+) to (n)text succeeds without failure only with 2005 and greater. - // it fails in sql server 2000 - maxSizeInBytes = (sizeInCharacters > actualSizeInBytes) ? sizeInCharacters : actualSizeInBytes; - } - - if ( - (maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || - HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || - (sizeInCharacters == -1) || - (actualSizeInBytes == -1) - ) - { // is size > size able to be described by 2 bytes - if (is2005OrNewer) - { - // Convert the parameter to its max type - mt = MetaType.GetMaxMetaTypeFromMetaType(mt); - _metaType = mt; - InternalMetaType = mt; - if (!mt.IsPlp) - { - if (mt.SqlDbType == SqlDbType.Xml) - { - throw ADP.InvalidMetaDataValue(); //Xml should always have IsPartialLength = true - } - if ( - mt.SqlDbType == SqlDbType.NVarChar || - mt.SqlDbType == SqlDbType.VarChar || - mt.SqlDbType == SqlDbType.VarBinary - ) - { - Size = (int)SmiMetaData.UnlimitedMaxLengthIndicator; - } - } - } - else - { - switch (mt.SqlDbType) - { // widening the SqlDbType is automatic - case SqlDbType.Binary: - case SqlDbType.VarBinary: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Image, false); - _metaType = mt; // do not use SqlDbType property which calls PropertyTypeChanging resetting coerced value - InternalMetaType = mt; - break; - case SqlDbType.Char: - case SqlDbType.VarChar: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Text, false); - _metaType = mt; - InternalMetaType = mt; - break; - case SqlDbType.NChar: - case SqlDbType.NVarChar: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.NText, false); - _metaType = mt; - InternalMetaType = mt; - break; - default: - Debug.Assert(false, "Missed metatype in SqlCommand.BuildParamList()"); - break; - } - } - } - } - return mt; - } - - private byte ValuePrecision(object value) - { - if (value is SqlDecimal sqlDecimal) - { - if (sqlDecimal.IsNull) - { - return 0; - } - return sqlDecimal.Precision; - } - return ValuePrecisionCore(value); - } - - private byte ValueScale(object value) - { - if (value is SqlDecimal sqlDecimal) - { - if (sqlDecimal.IsNull) - { - return 0; - } - return sqlDecimal.Scale; - } - return ValueScaleCore(value); - } - - private int ValueSize(object value) - { - if (value is SqlString sqlString) - { - if (sqlString.IsNull) - { - return 0; - } - return sqlString.Value.Length; - } - if (value is SqlChars sqlChars) - { - if (sqlChars.IsNull) - { - return 0; - } - return sqlChars.Value.Length; - } - - if (value is SqlBinary sqlBinary) - { - if (sqlBinary.IsNull) - { - return 0; - } - return sqlBinary.Length; - } - if (value is SqlBytes sqlBytes) - { - if (sqlBytes.IsNull) - { - return 0; - } - return (int)(sqlBytes.Length); - } - if (value is DataFeed) - { - // Unknown length - return 0; - } - return ValueSizeCore(value); - } - - private byte ValuePrecisionCore(object value) - { - if (value is decimal decimalValue) - { - return ((SqlDecimal)decimalValue).Precision; - } - return 0; - } - - private byte ValueScaleCore(object value) - { - if (value is decimal decimalValue) - { - return (byte)((decimal.GetBits(decimalValue)[3] & 0x00ff0000) >> 0x10); - } - return 0; - } - - private int ValueSizeCore(object value) - { - if (!ADP.IsNull(value)) - { - switch (value) - { - case string svalue: - return svalue.Length; - case byte[] bvalue: - return bvalue.Length; - case char[] cvalue: - return cvalue.Length; - case byte _: - case char _: - return 1; - } - } - return 0; - } - - - // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata) - internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) - { - Debug.Assert(!(value is DataFeed), "Value provided should not already be a data feed"); - Debug.Assert(!ADP.IsNull(value), "Value provided should not be null"); - Debug.Assert(null != destinationType, "null destinationType"); - - coercedToDataFeed = false; - typeChanged = false; - Type currentType = value.GetType(); - - if ( - (destinationType.ClassType != typeof(object)) && - (destinationType.ClassType != currentType) && - ( - (destinationType.SqlType != currentType) || - (destinationType.SqlDbType == SqlDbType.Xml) - ) - ) - { // Special case for Xml types (since we need to convert SqlXml into a string) - try - { - // Assume that the type changed - typeChanged = true; - if (destinationType.ClassType == typeof(string)) - { - // For Xml data, destination Type is always string - if (currentType == typeof(SqlXml)) - { - value = MetaType.GetStringFromXml((XmlReader)(((SqlXml)value).CreateReader())); - } - else if (currentType == typeof(SqlString)) - { - typeChanged = false; // Do nothing - } - else if (typeof(XmlReader).IsAssignableFrom(currentType)) - { - if (allowStreaming) - { - coercedToDataFeed = true; - value = new XmlDataFeed((XmlReader)value); - } - else - { - value = MetaType.GetStringFromXml((XmlReader)value); - } - } - else if (currentType == typeof(char[])) - { - value = new string((char[])value); - } - else if (currentType == typeof(SqlChars)) - { - value = new string(((SqlChars)value).Value); - } - else if (value is TextReader textReader && allowStreaming) - { - coercedToDataFeed = true; - value = new TextDataFeed(textReader); - } - else - { - value = Convert.ChangeType(value, destinationType.ClassType, null); - } - } - else if ((destinationType.DbType == DbType.Currency) && (currentType == typeof(string))) - { - value = decimal.Parse((string)value, NumberStyles.Currency, null); - } - else if ((currentType == typeof(SqlBytes)) && (destinationType.ClassType == typeof(byte[]))) - { - typeChanged = false; // Do nothing - } - else if ((currentType == typeof(string)) && (destinationType.SqlDbType == SqlDbType.Time)) - { - value = TimeSpan.Parse((string)value); - } - else if ((currentType == typeof(string)) && (destinationType.SqlDbType == SqlDbType.DateTimeOffset)) - { - value = DateTimeOffset.Parse((string)value, (IFormatProvider)null); - } - else if ((currentType == typeof(DateTime)) && (destinationType.SqlDbType == SqlDbType.DateTimeOffset)) - { - value = new DateTimeOffset((DateTime)value); - } - else if ( - TdsEnums.SQLTABLE == destinationType.TDSType && - ( - value is DataTable || - value is DbDataReader || - value is IEnumerable - ) - ) - { - // no conversion for TVPs. - typeChanged = false; - } - else if (destinationType.ClassType == typeof(byte[]) && allowStreaming && value is Stream stream) - { - coercedToDataFeed = true; - value = new StreamDataFeed(stream); - } - else - { - value = Convert.ChangeType(value, destinationType.ClassType, null); - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - - throw ADP.ParameterConversionFailed(value, destinationType.ClassType, e); - } - } - - Debug.Assert(allowStreaming || !coercedToDataFeed, "Streaming is not allowed, but type was coerced into a data feed"); - Debug.Assert(value.GetType() == currentType ^ typeChanged, "Incorrect value for typeChanged"); - return value; - } - - private static int StringSize(object value, bool isSqlType) - { - if (isSqlType) - { - Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values"); - if (value is SqlString sqlString) - { - return sqlString.Value.Length; - } - if (value is SqlChars sqlChars) - { - return sqlChars.Value.Length; - } - } - else - { - if (value is string svalue) - { - return svalue.Length; - } - if (value is char[] cvalue) - { - return cvalue.Length; - } - if (value is char) - { - return 1; - } - } - - // Didn't match, unknown size - return 0; - } - - private static int BinarySize(object value, bool isSqlType) - { - if (isSqlType) - { - Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values"); - if (value is SqlBinary sqlBinary) - { - return sqlBinary.Length; - } - if (value is SqlBytes sqlBytes) - { - return sqlBytes.Value.Length; - } - } - else - { - if (value is byte[] bvalue) - { - return bvalue.Length; - } - if (value is byte) - { - return 1; - } - } - - // Didn't match, unknown size - return 0; - } - - // parse an string of the form db.schema.name where any of the three components - // might have "[" "]" and dots within it. - // returns: - // [0] dbname (or null) - // [1] schema (or null) - // [2] name - // NOTE: if perf/space implications of Regex is not a problem, we can get rid - // of this and use a simple regex to do the parsing - internal static string[] ParseTypeName(string typeName, bool isUdtTypeName) - { - Debug.Assert(null != typeName, "null typename passed to ParseTypeName"); - - try - { - string errorMsg = isUdtTypeName ? Strings.SQL_UDTTypeName : Strings.SQL_TypeName; - return MultipartIdentifier.ParseMultipartIdentifier(typeName, "[\"", "]\"", '.', 3, true, errorMsg, true); - } - catch (ArgumentException) - { - if (isUdtTypeName) - { - throw SQL.InvalidUdt3PartNameFormat(); - } - else - { - throw SQL.InvalidParameterTypeNameFormat(); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs deleted file mode 100644 index b1b4a4fed2..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs +++ /dev/null @@ -1,542 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - sealed internal class SqlSequentialTextReader : System.IO.TextReader - { - private SqlDataReader _reader; // The SqlDataReader that we are reading data from - private int _columnIndex; // The index of out column in the table - private Encoding _encoding; // Encoding for this character stream - private Decoder _decoder; // Decoder based on the encoding (NOTE: Decoders are stateful as they are designed to process streams of data) - private byte[] _leftOverBytes; // Bytes leftover from the last Read() operation - this can be null if there were no bytes leftover (Possible optimization: re-use the same array?) - private int _peekedChar; // The last character that we peeked at (or -1 if we haven't peeked at anything) - private Task _currentTask; // The current async task - private CancellationTokenSource _disposalTokenSource; // Used to indicate that a cancellation is requested due to disposal - - internal SqlSequentialTextReader(SqlDataReader reader, int columnIndex, Encoding encoding) - { - Debug.Assert(reader != null, "Null reader when creating sequential textreader"); - Debug.Assert(columnIndex >= 0, "Invalid column index when creating sequential textreader"); - Debug.Assert(encoding != null, "Null encoding when creating sequential textreader"); - - _reader = reader; - _columnIndex = columnIndex; - _encoding = encoding; - _decoder = encoding.GetDecoder(); - _leftOverBytes = null; - _peekedChar = -1; - _currentTask = null; - _disposalTokenSource = new CancellationTokenSource(); - } - - internal int ColumnIndex - { - get { return _columnIndex; } - } - - public override int Peek() - { - if (_currentTask != null) - { - throw ADP.AsyncOperationPending(); - } - if (IsClosed) - { - throw ADP.ObjectDisposed(this); - } - - if (!HasPeekedChar) - { - _peekedChar = Read(); - } - - Debug.Assert(_peekedChar == -1 || ((_peekedChar >= char.MinValue) && (_peekedChar <= char.MaxValue)), $"Bad peeked character: {_peekedChar}"); - return _peekedChar; - } - - public override int Read() - { - if (_currentTask != null) - { - throw ADP.AsyncOperationPending(); - } - if (IsClosed) - { - throw ADP.ObjectDisposed(this); - } - - int readChar = -1; - - // If there is already a peeked char, then return it - if (HasPeekedChar) - { - readChar = _peekedChar; - _peekedChar = -1; - } - // If there is data available try to read a char - else - { - char[] tempBuffer = new char[1]; - int charsRead = InternalRead(tempBuffer, 0, 1); - if (charsRead == 1) - { - readChar = tempBuffer[0]; - } - } - - Debug.Assert(readChar == -1 || ((readChar >= char.MinValue) && (readChar <= char.MaxValue)), $"Bad read character: {readChar}"); - return readChar; - } - - public override int Read(char[] buffer, int index, int count) - { - ValidateReadParameters(buffer, index, count); - - if (IsClosed) - { - throw ADP.ObjectDisposed(this); - } - if (_currentTask != null) - { - throw ADP.AsyncOperationPending(); - } - - int charsRead = 0; - int charsNeeded = count; - // Load in peeked char - if ((charsNeeded > 0) && (HasPeekedChar)) - { - Debug.Assert((_peekedChar >= char.MinValue) && (_peekedChar <= char.MaxValue), $"Bad peeked character: {_peekedChar}"); - buffer[index + charsRead] = (char)_peekedChar; - charsRead++; - charsNeeded--; - _peekedChar = -1; - } - - // If we need more data and there is data avaiable, read - charsRead += InternalRead(buffer, index + charsRead, charsNeeded); - - return charsRead; - } - - public override Task ReadAsync(char[] buffer, int index, int count) - { - ValidateReadParameters(buffer, index, count); - TaskCompletionSource completion = new TaskCompletionSource(); - - if (IsClosed) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - else - { - try - { - Task original = Interlocked.CompareExchange(ref _currentTask, completion.Task, null); - if (original != null) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending())); - } - else - { - bool completedSynchronously = true; - int charsRead = 0; - int adjustedIndex = index; - int charsNeeded = count; - - // Load in peeked char - if ((HasPeekedChar) && (charsNeeded > 0)) - { - // Take a copy of _peekedChar in case it is cleared during close - int peekedChar = _peekedChar; - if (peekedChar >= char.MinValue) - { - Debug.Assert((_peekedChar >= char.MinValue) && (_peekedChar <= char.MaxValue), $"Bad peeked character: {_peekedChar}"); - buffer[adjustedIndex] = (char)peekedChar; - adjustedIndex++; - charsRead++; - charsNeeded--; - _peekedChar = -1; - } - } - - int byteBufferUsed; - byte[] byteBuffer = PrepareByteBuffer(charsNeeded, out byteBufferUsed); - - // Permit a 0 byte read in order to advance the reader to the correct column - if ((byteBufferUsed < byteBuffer.Length) || (byteBuffer.Length == 0)) - { - int bytesRead; - var reader = _reader; - if (reader != null) - { - Task getBytesTask = reader.GetBytesAsync(_columnIndex, byteBuffer, byteBufferUsed, byteBuffer.Length - byteBufferUsed, Timeout.Infinite, _disposalTokenSource.Token, out bytesRead); - if (getBytesTask == null) - { - byteBufferUsed += bytesRead; - } - else - { - // We need more data - setup the callback, and mark this as not completed sync - completedSynchronously = false; - getBytesTask.ContinueWith((t) => - { - _currentTask = null; - // If we completed but the textreader is closed, then report cancellation - if ((t.Status == TaskStatus.RanToCompletion) && (!IsClosed)) - { - try - { - int bytesReadFromStream = t.Result; - byteBufferUsed += bytesReadFromStream; - if (byteBufferUsed > 0) - { - charsRead += DecodeBytesToChars(byteBuffer, byteBufferUsed, buffer, adjustedIndex, charsNeeded); - } - completion.SetResult(charsRead); - } - catch (Exception ex) - { - completion.SetException(ex); - } - } - else if (IsClosed) - { - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - else if (t.Status == TaskStatus.Faulted) - { - if (t.Exception.InnerException is SqlException) - { - // ReadAsync can't throw a SqlException, so wrap it in an IOException - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ErrorReadingFromStream(t.Exception.InnerException))); - } - else - { - completion.SetException(t.Exception.InnerException); - } - } - else - { - completion.SetCanceled(); - } - }, TaskScheduler.Default); - } - - - if ((completedSynchronously) && (byteBufferUsed > 0)) - { - // No more data needed, decode what we have - charsRead += DecodeBytesToChars(byteBuffer, byteBufferUsed, buffer, adjustedIndex, charsNeeded); - } - } - else - { - // Reader is null, close must of happened in the middle of this read - completion.SetException(ADP.ExceptionWithStackTrace(ADP.ObjectDisposed(this))); - } - } - - - if (completedSynchronously) - { - _currentTask = null; - if (IsClosed) - { - completion.SetCanceled(); - } - else - { - completion.SetResult(charsRead); - } - } - } - } - catch (Exception ex) - { - // In case of any errors, ensure that the completion is completed and the task is set back to null if we switched it - completion.TrySetException(ex); - Interlocked.CompareExchange(ref _currentTask, null, completion.Task); - throw; - } - } - - return completion.Task; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - // Set the textreader as closed - SetClosed(); - } - - base.Dispose(disposing); - } - - /// - /// Forces the TextReader to act as if it was closed - /// This does not actually close the stream, read off the rest of the data or dispose this - /// - internal void SetClosed() - { - _disposalTokenSource.Cancel(); - _reader = null; - _peekedChar = -1; - - // Wait for pending task - var currentTask = _currentTask; - if (currentTask != null) - { - ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne(); - } - } - - /// - /// Performs the actual reading and converting - /// NOTE: This assumes that buffer, index and count are all valid, we're not closed (!IsClosed) and that there is data left (IsDataLeft()) - /// - /// - /// - /// - /// - private int InternalRead(char[] buffer, int index, int count) - { - Debug.Assert(buffer != null, "Null output buffer"); - Debug.Assert((index >= 0) && (count >= 0) && (index + count <= buffer.Length), $"Bad count: {count} or index: {index}"); - Debug.Assert(!IsClosed, "Can't read while textreader is closed"); - - try - { - int byteBufferUsed; - byte[] byteBuffer = PrepareByteBuffer(count, out byteBufferUsed); - byteBufferUsed += _reader.GetBytesInternalSequential(_columnIndex, byteBuffer, byteBufferUsed, byteBuffer.Length - byteBufferUsed); - - if (byteBufferUsed > 0) - { - return DecodeBytesToChars(byteBuffer, byteBufferUsed, buffer, index, count); - } - else - { - // Nothing to read, or nothing read - return 0; - } - } - catch (SqlException ex) - { - // Read can't throw a SqlException - so wrap it in an IOException - throw ADP.ErrorReadingFromStream(ex); - } - } - - /// - /// Creates a byte array large enough to store all bytes for the characters in the current encoding, then fills it with any leftover bytes - /// - /// Number of characters that are to be read - /// Number of bytes pre-filled by the leftover bytes - /// A byte array of the correct size, pre-filled with leftover bytes - private byte[] PrepareByteBuffer(int numberOfChars, out int byteBufferUsed) - { - Debug.Assert(numberOfChars >= 0, "Can't prepare a byte buffer for negative characters"); - - byte[] byteBuffer; - - if (numberOfChars == 0) - { - byteBuffer = new byte[0]; - byteBufferUsed = 0; - } - else - { - int byteBufferSize = _encoding.GetMaxByteCount(numberOfChars); - - if (_leftOverBytes != null) - { - // If we have more leftover bytes than we need for this conversion, then just re-use the leftover buffer - if (_leftOverBytes.Length > byteBufferSize) - { - byteBuffer = _leftOverBytes; - byteBufferUsed = byteBuffer.Length; - } - else - { - // Otherwise, copy over the leftover buffer - byteBuffer = new byte[byteBufferSize]; - Array.Copy(_leftOverBytes, byteBuffer, _leftOverBytes.Length); - byteBufferUsed = _leftOverBytes.Length; - } - } - else - { - byteBuffer = new byte[byteBufferSize]; - byteBufferUsed = 0; - } - } - - return byteBuffer; - } - - /// - /// Decodes the given bytes into characters, and stores the leftover bytes for later use - /// - /// Buffer of bytes to decode - /// Number of bytes to decode from the inBuffer - /// Buffer to write the characters to - /// Offset to start writing to outBuffer at - /// Maximum number of characters to decode - /// The actual number of characters decoded - private int DecodeBytesToChars(byte[] inBuffer, int inBufferCount, char[] outBuffer, int outBufferOffset, int outBufferCount) - { - Debug.Assert(inBuffer != null, "Null input buffer"); - Debug.Assert((inBufferCount > 0) && (inBufferCount <= inBuffer.Length), $"Bad inBufferCount: {inBufferCount}"); - Debug.Assert(outBuffer != null, "Null output buffer"); - Debug.Assert((outBufferOffset >= 0) && (outBufferCount > 0) && (outBufferOffset + outBufferCount <= outBuffer.Length), $"Bad outBufferCount: {outBufferCount} or outBufferOffset: {outBufferOffset}"); - - int charsRead; - int bytesUsed; - bool completed; - _decoder.Convert(inBuffer, 0, inBufferCount, outBuffer, outBufferOffset, outBufferCount, false, out bytesUsed, out charsRead, out completed); - - // completed may be false and there is no spare bytes if the Decoder has stored bytes to use later - if ((!completed) && (bytesUsed < inBufferCount)) - { - _leftOverBytes = new byte[inBufferCount - bytesUsed]; - Array.Copy(inBuffer, bytesUsed, _leftOverBytes, 0, _leftOverBytes.Length); - } - else - { - // If Convert() sets completed to true, then it must have used all of the bytes we gave it - Debug.Assert(bytesUsed >= inBufferCount, "Converted completed, but not all bytes were used"); - _leftOverBytes = null; - } - - Debug.Assert(((_reader == null) || (_reader.ColumnDataBytesRemaining() > 0) || (!completed) || (_leftOverBytes == null)), "Stream has run out of data and the decoder finished, but there are leftover bytes"); - Debug.Assert(charsRead > 0, "Converted no chars. Bad encoding?"); - - return charsRead; - } - - /// - /// True if this TextReader is supposed to be closed - /// - private bool IsClosed - { - get { return (_reader == null); } - } - - /// - /// True if there is data left to read - /// - /// - private bool IsDataLeft - { - get { return ((_leftOverBytes != null) || (_reader.ColumnDataBytesRemaining() > 0)); } - } - - /// - /// True if there is a peeked character available - /// - private bool HasPeekedChar - { - get { return (_peekedChar >= char.MinValue); } - } - - /// - /// Checks the the parameters passed into a Read() method are valid - /// - /// - /// - /// - internal static void ValidateReadParameters(char[] buffer, int index, int count) - { - if (buffer == null) - { - throw ADP.ArgumentNull(nameof(buffer)); - } - if (index < 0) - { - throw ADP.ArgumentOutOfRange(nameof(index)); - } - if (count < 0) - { - throw ADP.ArgumentOutOfRange(nameof(count)); - } - try - { - if (checked(index + count) > buffer.Length) - { - throw ExceptionBuilder.InvalidOffsetLength(); - } - } - catch (OverflowException) - { - // If we've overflowed when adding index and count, then they never would have fit into buffer anyway - throw ExceptionBuilder.InvalidOffsetLength(); - } - } - } - - sealed internal class SqlUnicodeEncoding : UnicodeEncoding - { - private static SqlUnicodeEncoding _singletonEncoding = new SqlUnicodeEncoding(); - - private SqlUnicodeEncoding() : base(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: false) - { } - - public override Decoder GetDecoder() - { - return new SqlUnicodeDecoder(); - } - - public override int GetMaxByteCount(int charCount) - { - // SQL Server never sends a BOM, so we can assume that its 2 bytes per char - return charCount * 2; - } - - public static Encoding SqlUnicodeEncodingInstance - { - get { return _singletonEncoding; } - } - - sealed private class SqlUnicodeDecoder : Decoder - { - public override int GetCharCount(byte[] bytes, int index, int count) - { - // SQL Server never sends a BOM, so we can assume that its 2 bytes per char - return count / 2; - } - - public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) - { - // This method is required - simply call Convert() - int bytesUsed; - int charsUsed; - bool completed; - Convert(bytes, byteIndex, byteCount, chars, charIndex, chars.Length - charIndex, true, out bytesUsed, out charsUsed, out completed); - return charsUsed; - } - - public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex, int charCount, bool flush, out int bytesUsed, out int charsUsed, out bool completed) - { - // Assume 2 bytes per char and no BOM - charsUsed = Math.Min(charCount, byteCount / 2); - bytesUsed = charsUsed * 2; - completed = (bytesUsed == byteCount); - - // BlockCopy uses offsets\length measured in bytes, not the actual array index - Buffer.BlockCopy(bytes, byteIndex, chars, charIndex * 2, bytesUsed); - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStream.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStream.cs deleted file mode 100644 index 4e0f723361..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStream.cs +++ /dev/null @@ -1,734 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; -using System.Xml; -using Microsoft.Data.Common; -using Microsoft.Data.SqlTypes; - -namespace Microsoft.Data.SqlClient -{ - sealed internal class SqlStream : Stream - { - private SqlDataReader _reader; // reader we will stream off - private int _columnOrdinal; - private long _bytesCol; - int _bom; - private byte[] _bufferedData; - private bool _processAllRows; - private bool _advanceReader; - private bool _readFirstRow = false; - private bool _endOfColumn = false; - - internal SqlStream(SqlDataReader reader, bool addByteOrderMark, bool processAllRows) : - this(0, reader, addByteOrderMark, processAllRows, true) - { - } - - internal SqlStream(int columnOrdinal, SqlDataReader reader, bool addByteOrderMark, bool processAllRows, bool advanceReader) - { - _columnOrdinal = columnOrdinal; - _reader = reader; - _bom = addByteOrderMark ? 0xfeff : 0; - _processAllRows = processAllRows; - _advanceReader = advanceReader; - } - - override public bool CanRead - { - get - { - return true; - } - } - - override public bool CanSeek - { - get - { - return false; - } - } - - override public bool CanWrite - { - get - { - return false; - } - } - - override public long Length - { - get - { - throw ADP.NotSupported(); - } - } - - override public long Position - { - get - { - throw ADP.NotSupported(); - } - set - { - throw ADP.NotSupported(); - } - } - - override protected void Dispose(bool disposing) - { - try - { - if (disposing && _advanceReader && _reader != null && !_reader.IsClosed) - { - _reader.Close(); - } - _reader = null; - } - finally - { - base.Dispose(disposing); - } - } - - override public void Flush() - { - throw ADP.NotSupported(); - } - - override public int Read(byte[] buffer, int offset, int count) - { - int intCount = 0; - int cBufferedData = 0; - - if ((null == _reader)) - { - throw ADP.StreamClosed(nameof(Read)); - } - if (null == buffer) - { - throw ADP.ArgumentNull(nameof(buffer)); - } - if ((offset < 0) || (count < 0)) - { - throw ADP.ArgumentOutOfRange(string.Empty, (offset < 0 ? nameof(offset) : nameof(count))); - } - if (buffer.Length - offset < count) - { - throw ADP.ArgumentOutOfRange(nameof(count)); - } - - // Need to find out if we should add byte order mark or not. - // We need to add this if we are getting ntext xml, not if we are getting binary xml - // Binary Xml always begins with the bytes 0xDF and 0xFF - // If we aren't getting these, then we are getting unicode xml - if (_bom > 0) - { - // Read and buffer the first two bytes - _bufferedData = new byte[2]; - cBufferedData = ReadBytes(_bufferedData, 0, 2); - // Check to se if we should add the byte order mark - if ((cBufferedData < 2) || ((_bufferedData[0] == 0xDF) && (_bufferedData[1] == 0xFF))) - { - _bom = 0; - } - while (count > 0) - { - if (_bom > 0) - { - buffer[offset] = (byte)_bom; - _bom >>= 8; - offset++; - count--; - intCount++; - } - else - { - break; - } - } - } - - if (cBufferedData > 0) - { - while (count > 0) - { - buffer[offset++] = _bufferedData[0]; - intCount++; - count--; - if ((cBufferedData > 1) && (count > 0)) - { - buffer[offset++] = _bufferedData[1]; - intCount++; - count--; - break; - } - } - _bufferedData = null; - } - - intCount += ReadBytes(buffer, offset, count); - - return intCount; - } - - private static bool AdvanceToNextRow(SqlDataReader reader) - { - Debug.Assert(reader != null && !reader.IsClosed); - - // this method skips empty result sets - do - { - if (reader.Read()) - { - return true; - } - } while (reader.NextResult()); - - // no more rows - return false; - } - - private int ReadBytes(byte[] buffer, int offset, int count) - { - bool gotData = true; - int intCount = 0; - int cb = 0; - - if (_reader.IsClosed || _endOfColumn) - { - return 0; - } - try - { - while (count > 0) - { - // if I haven't read any bytes, get the next row - if (_advanceReader && (0 == _bytesCol)) - { - gotData = false; - - if (_readFirstRow && !_processAllRows) - { - // for XML column, stop processing after the first row - // no op here - reader is closed after the end of this loop - } - else if (AdvanceToNextRow(_reader)) - { - _readFirstRow = true; - - if (_reader.IsDBNull(_columnOrdinal)) - { - // VSTFDEVDIV 479659: handle row with DBNULL as empty data - // for XML column, processing is stopped on the next loop since _readFirstRow is true - continue; - } - - // the value is not null, read it - gotData = true; - } - // else AdvanceToNextRow has returned false - no more rows or result sets remained, stop processing - } - - if (gotData) - { - cb = (int)_reader.GetBytesInternal(_columnOrdinal, _bytesCol, buffer, offset, count); - - if (cb < count) - { - _bytesCol = 0; - gotData = false; - if (!_advanceReader) - { - _endOfColumn = true; - } - } - else - { - Debug.Assert(cb == count); - _bytesCol += cb; - } - - // we are guaranteed that cb is < Int32.Max since we always pass in count which is of type Int32 to - // our getbytes interface - count -= (int)cb; - offset += (int)cb; - intCount += (int)cb; - } - else - { - break; // no more data available, we are done - } - } - if (!gotData && _advanceReader) - { - _reader.Close(); // Need to close the reader if we are done reading - } - } - catch (Exception e) - { - if (_advanceReader && ADP.IsCatchableExceptionType(e)) - { - _reader.Close(); - } - throw; - } - - return intCount; - } - - internal XmlReader ToXmlReader(bool async = false) - { - // Dev11 Bug #315513: Exception type breaking change from 4.0 RTM when calling GetChars on null xml - // We need to wrap all exceptions inside a TargetInvocationException to simulate calling CreateSqlReader via MethodInfo.Invoke - return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(this, closeInput: true, async: async); - } - - override public long Seek(long offset, SeekOrigin origin) - { - throw ADP.NotSupported(); - } - - override public void SetLength(long value) - { - throw ADP.NotSupported(); - } - - override public void Write(byte[] buffer, int offset, int count) - { - throw ADP.NotSupported(); - } - } - - - // XmlTextReader does not read all the bytes off the network buffers, so we have to cache it here in the random access - // case. This causes double buffering and is a perf hit, but this is not the high perf way for accessing this type of data. - // In the case of sequential access, we do not have to do any buffering since the XmlTextReader we return can become - // invalid as soon as we move off the current column. - sealed internal class SqlCachedStream : Stream - { - int _currentPosition; // Position within the current array byte - int _currentArrayIndex; // Index into the _cachedBytes ArrayList - List _cachedBytes; - long _totalLength; - - // Reads off from the network buffer and caches bytes. Only reads one column value in the current row. - internal SqlCachedStream(SqlCachedBuffer sqlBuf) - { - _cachedBytes = sqlBuf.CachedBytes; - } - - override public bool CanRead - { - get - { - return true; - } - } - - override public bool CanSeek - { - get - { - return true; - } - } - - override public bool CanWrite - { - get - { - return false; - } - } - - override public long Length - { - get - { - return TotalLength; - } - } - - override public long Position - { - get - { - long pos = 0; - if (_currentArrayIndex > 0) - { - for (int ii = 0; ii < _currentArrayIndex; ii++) - { - pos += _cachedBytes[ii].Length; - } - } - pos += _currentPosition; - return pos; - } - set - { - if (null == _cachedBytes) - { - throw ADP.StreamClosed(ADP.ParameterSetPosition); - } - SetInternalPosition(value, ADP.ParameterSetPosition); - } - } - - override protected void Dispose(bool disposing) - { - try - { - if (disposing && _cachedBytes != null) - _cachedBytes.Clear(); - _cachedBytes = null; - _currentPosition = 0; - _currentArrayIndex = 0; - _totalLength = 0; - } - finally - { - base.Dispose(disposing); - } - } - - override public void Flush() - { - throw ADP.NotSupported(); - } - - override public int Read(byte[] buffer, int offset, int count) - { - int cb; - int intCount = 0; - - if (null == _cachedBytes) - { - throw ADP.StreamClosed(nameof(Read)); - } - - if (null == buffer) - { - throw ADP.ArgumentNull(nameof(buffer)); - } - - if ((offset < 0) || (count < 0)) - { - throw ADP.ArgumentOutOfRange(string.Empty, (offset < 0 ? nameof(offset) : nameof(count))); - } - - if (buffer.Length - offset < count) - { - throw ADP.ArgumentOutOfRange(nameof(count)); - } - - if (_cachedBytes.Count <= _currentArrayIndex) - { - return 0; // Everything is read! - } - - while (count > 0) - { - if (_cachedBytes[_currentArrayIndex].Length <= _currentPosition) - { - _currentArrayIndex++; // We are done reading this chunk, go to next - if (_cachedBytes.Count > _currentArrayIndex) - { - _currentPosition = 0; - } - else - { - break; - } - } - cb = _cachedBytes[_currentArrayIndex].Length - _currentPosition; - if (cb > count) - cb = count; - Array.Copy(_cachedBytes[_currentArrayIndex], _currentPosition, buffer, offset, cb); - - _currentPosition += cb; - count -= (int)cb; - offset += (int)cb; - intCount += (int)cb; - } - - return intCount; - } - - override public long Seek(long offset, SeekOrigin origin) - { - long pos = 0; - - if (null == _cachedBytes) - { - throw ADP.StreamClosed(nameof(Read)); - } - - switch (origin) - { - case SeekOrigin.Begin: - SetInternalPosition(offset, nameof(offset)); - break; - - case SeekOrigin.Current: - pos = offset + Position; - SetInternalPosition(pos, nameof(offset)); - break; - - case SeekOrigin.End: - pos = TotalLength + offset; - SetInternalPosition(pos, nameof(offset)); - break; - - default: - throw ADP.InvalidSeekOrigin(nameof(offset)); - } - return pos; - } - - override public void SetLength(long value) - { - throw ADP.NotSupported(); - } - - override public void Write(byte[] buffer, int offset, int count) - { - throw ADP.NotSupported(); - } - - private void SetInternalPosition(long lPos, string argumentName) - { - long pos = lPos; - - if (pos < 0) - { - throw new ArgumentOutOfRangeException(argumentName); - } - for (int ii = 0; ii < _cachedBytes.Count; ii++) - { - if (pos > _cachedBytes[ii].Length) - { - pos -= _cachedBytes[ii].Length; - } - else - { - _currentArrayIndex = ii; - _currentPosition = (int)pos; - return; - } - } - if (pos > 0) - throw new ArgumentOutOfRangeException(argumentName); - } - - private long TotalLength - { - get - { - if ((_totalLength == 0) && (_cachedBytes != null)) - { - long pos = 0; - for (int ii = 0; ii < _cachedBytes.Count; ii++) - { - pos += _cachedBytes[ii].Length; - } - _totalLength = pos; - } - return _totalLength; - } - } - } - - sealed internal class SqlStreamingXml - { - - int _columnOrdinal; - SqlDataReader _reader; - XmlReader _xmlReader; - XmlWriter _xmlWriter; - StringWriter _strWriter; - long _charsRemoved; - - public SqlStreamingXml(int i, SqlDataReader reader) - { - _columnOrdinal = i; - _reader = reader; - } - - public void Close() - { - ((IDisposable)_xmlWriter).Dispose(); - ((IDisposable)_xmlReader).Dispose(); - _reader = null; - _xmlReader = null; - _xmlWriter = null; - _strWriter = null; - } - - public int ColumnOrdinal - { - get - { - return _columnOrdinal; - } - } - - public long GetChars(long dataIndex, char[] buffer, int bufferIndex, int length) - { - if (_xmlReader == null) - { - SqlStream sqlStream = new SqlStream(_columnOrdinal, _reader, true /* addByteOrderMark */, false /* processAllRows*/, false /*advanceReader*/); - _xmlReader = sqlStream.ToXmlReader(); - _strWriter = new StringWriter((System.IFormatProvider)null); - XmlWriterSettings writerSettings = new XmlWriterSettings(); - writerSettings.CloseOutput = true; // close the memory stream when done - writerSettings.ConformanceLevel = ConformanceLevel.Fragment; - _xmlWriter = XmlWriter.Create(_strWriter, writerSettings); - } - - int charsToSkip = 0; - int cnt = 0; - if (dataIndex < _charsRemoved) - { - throw ADP.NonSeqByteAccess(dataIndex, _charsRemoved, nameof(GetChars)); - } - else if (dataIndex > _charsRemoved) - { - charsToSkip = (int)(dataIndex - _charsRemoved); - } - - // If buffer parameter is null, we have to return -1 since there is no way for us to know the - // total size up front without reading and converting the XML. - if (buffer == null) - { - return (long)(-1); - } - - StringBuilder strBldr = _strWriter.GetStringBuilder(); - while (!_xmlReader.EOF) - { - if (strBldr.Length >= (length + charsToSkip)) - { - break; - } - // Can't call _xmlWriter.WriteNode here, since it reads all of the data in before returning the first char. - // Do own implementation of WriteNode instead that reads just enough data to return the required number of chars - //_xmlWriter.WriteNode(_xmlReader, true); - // _xmlWriter.Flush(); - WriteXmlElement(); - if (charsToSkip > 0) - { - // Aggressively remove the characters we want to skip to avoid growing StringBuilder size too much - cnt = strBldr.Length < charsToSkip ? strBldr.Length : charsToSkip; - strBldr.Remove(0, cnt); - charsToSkip -= cnt; - _charsRemoved += (long)cnt; - } - } - - if (charsToSkip > 0) - { - cnt = strBldr.Length < charsToSkip ? strBldr.Length : charsToSkip; - strBldr.Remove(0, cnt); - charsToSkip -= cnt; - _charsRemoved += (long)cnt; - } - - if (strBldr.Length == 0) - { - return 0; - } - // At this point charsToSkip must be 0 - Debug.Assert(charsToSkip == 0); - - cnt = strBldr.Length < length ? strBldr.Length : length; - for (int i = 0; i < cnt; i++) - { - buffer[bufferIndex + i] = strBldr[i]; - } - // Remove the characters we have already returned - strBldr.Remove(0, cnt); - _charsRemoved += (long)cnt; - return (long)cnt; - } - - // This method duplicates the work of XmlWriter.WriteNode except that it reads one element at a time - // instead of reading the entire node like XmlWriter. - private void WriteXmlElement() - { - - if (_xmlReader.EOF) - return; - - bool canReadChunk = _xmlReader.CanReadValueChunk; - char[] writeNodeBuffer = null; - - // Constants - const int WriteNodeBufferSize = 1024; - - _xmlReader.Read(); - switch (_xmlReader.NodeType) - { - case XmlNodeType.Element: - _xmlWriter.WriteStartElement(_xmlReader.Prefix, _xmlReader.LocalName, _xmlReader.NamespaceURI); - _xmlWriter.WriteAttributes(_xmlReader, true); - if (_xmlReader.IsEmptyElement) - { - _xmlWriter.WriteEndElement(); - break; - } - break; - case XmlNodeType.Text: - if (canReadChunk) - { - if (writeNodeBuffer == null) - { - writeNodeBuffer = new char[WriteNodeBufferSize]; - } - int read; - while ((read = _xmlReader.ReadValueChunk(writeNodeBuffer, 0, WriteNodeBufferSize)) > 0) - { - _xmlWriter.WriteChars(writeNodeBuffer, 0, read); - } - } - else - { - _xmlWriter.WriteString(_xmlReader.Value); - } - break; - case XmlNodeType.Whitespace: - case XmlNodeType.SignificantWhitespace: - _xmlWriter.WriteWhitespace(_xmlReader.Value); - break; - case XmlNodeType.CDATA: - _xmlWriter.WriteCData(_xmlReader.Value); - break; - case XmlNodeType.EntityReference: - _xmlWriter.WriteEntityRef(_xmlReader.Name); - break; - case XmlNodeType.XmlDeclaration: - case XmlNodeType.ProcessingInstruction: - _xmlWriter.WriteProcessingInstruction(_xmlReader.Name, _xmlReader.Value); - break; - case XmlNodeType.DocumentType: - _xmlWriter.WriteDocType(_xmlReader.Name, _xmlReader.GetAttribute("PUBLIC"), _xmlReader.GetAttribute("SYSTEM"), _xmlReader.Value); - break; - case XmlNodeType.Comment: - _xmlWriter.WriteComment(_xmlReader.Value); - break; - case XmlNodeType.EndElement: - _xmlWriter.WriteFullEndElement(); - break; - } - _xmlWriter.Flush(); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs index f344f9cdba..d5a93b1b86 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -3,139 +3,21 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; -using System.Data; using System.Data.Common; -using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Data.Common; - namespace Microsoft.Data.SqlClient { - /// - public sealed class SqlTransaction : DbTransaction + /// + public sealed partial class SqlTransaction : DbTransaction { - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { - SqlConnection.VerifyExecutePermission(); - - _isolationLevel = iso; - _connection = con; - - if (internalTransaction == null) - { - _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); - } - else - { - Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); - _internalTransaction = internalTransaction; - _internalTransaction.InitParent(this); - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PROPERTIES - //////////////////////////////////////////////////////////////////////////////////////// - - /// - new public SqlConnection Connection - { // MDAC 66655 - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - override protected DbConnection DbConnection - { - get - { - return Connection; - } - } - - internal SqlInternalTransaction InternalTransaction - { - get - { - return _internalTransaction; - } - } - - /// - override public IsolationLevel IsolationLevel - { - get - { - ZombieCheck(); - return _isolationLevel; - } - } - - private bool Is2005PartialZombie - { - get - { - return (null != _internalTransaction && _internalTransaction.IsCompleted); - } - } - - internal bool IsZombied - { - get - { - return (null == _internalTransaction || _internalTransaction.IsCompleted); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal SqlStatistics Statistics - { - get - { - if (null != _connection) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } - //////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// - /// - override public void Commit() + /// + public override void Commit() { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 @@ -151,14 +33,14 @@ override public void Commit() try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -195,8 +77,7 @@ override public void Commit() // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, // this connection may not be in reusable state. // We will abort this connection and make sure it does not go back to the pool. - var innerException = e.InnerException as Win32Exception; - if (innerException != null && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) + if (e.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) { _connection.Abort(e); } @@ -211,7 +92,7 @@ override public void Commit() } } - /// + /// protected override void Dispose(bool disposing) { if (disposing) @@ -221,7 +102,7 @@ protected override void Dispose(bool disposing) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try @@ -263,8 +144,8 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - /// - override public void Rollback() + /// + public override void Rollback() { if (Is2005PartialZombie) { @@ -287,14 +168,14 @@ override public void Rollback() try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -336,7 +217,7 @@ override public void Rollback() } } - /// + /// public void Rollback(string transactionName) { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 @@ -351,14 +232,14 @@ public void Rollback(string transactionName) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -399,7 +280,7 @@ public void Rollback(string transactionName) } } - /// + /// public void Save(string savePointName) { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 @@ -415,14 +296,14 @@ public void Save(string savePointName) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -458,49 +339,5 @@ public void Save(string savePointName) } } } - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - internal void Zombie() - { - // SQLBUDT #402544 For 2005, we have to defer "zombification" until - // we get past the users' next rollback, else we'll - // throw an exception there that is a breaking change. - // Of course, if the connection is aready closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - - if (null != internalConnection && internalConnection.Is2005OrNewer && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} 2005 deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre-2005 zombification - } - - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PRIVATE METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - private void ZombieCheck() - { - // If this transaction has been completed, throw exception since it is unusable. - if (IsZombied) - { - - if (Is2005PartialZombie) - { - _internalTransaction = null; // 2005 zombification - } - - throw ADP.TransactionZombied(this); - } - } } } - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs index 6c60e9e009..4cd10a988b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlUtil.cs @@ -108,13 +108,15 @@ internal static void ContinueTask(Task task, #if DEBUG TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); RuntimeHelpers.PrepareConstrainedRegions(); - try { + try + { tdsReliabilitySection.Start(); #endif //DEBUG - onSuccess(); + onSuccess(); #if DEBUG } - finally { + finally + { tdsReliabilitySection.Stop(); } #endif //DEBUG @@ -300,7 +302,7 @@ internal static void ContinueTaskWithState(Task task, } } } - }, + }, state: state, scheduler: TaskScheduler.Default ); @@ -654,6 +656,11 @@ static internal Exception ActiveDirectoryDeviceFlowTimeout() return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_DeviceFlow_Authentication); } + internal static Exception ActiveDirectoryTokenRetrievingTimeout(string authenticaton, string errorCode, Exception exception) + { + return ADP.TimeoutException(StringsHelper.GetString(Strings.AAD_Token_Retrieving_Timeout, authenticaton, errorCode, exception?.Message), exception); + } + // // SQL.DataCommand // @@ -670,16 +677,17 @@ static internal ArgumentOutOfRangeException NotSupportedEnumerationValue(Type ty static internal ArgumentOutOfRangeException NotSupportedCommandType(CommandType value) { #if DEBUG - switch(value) { - case CommandType.Text: - case CommandType.StoredProcedure: - Debug.Fail("valid CommandType " + value.ToString()); - break; - case CommandType.TableDirect: - break; - default: - Debug.Fail("invalid CommandType " + value.ToString()); - break; + switch (value) + { + case CommandType.Text: + case CommandType.StoredProcedure: + Debug.Fail("valid CommandType " + value.ToString()); + break; + case CommandType.TableDirect: + break; + default: + Debug.Fail("invalid CommandType " + value.ToString()); + break; } #endif return NotSupportedEnumerationValue(typeof(CommandType), (int)value); @@ -687,20 +695,21 @@ static internal ArgumentOutOfRangeException NotSupportedCommandType(CommandType static internal ArgumentOutOfRangeException NotSupportedIsolationLevel(IsolationLevel value) { #if DEBUG - switch(value) { - case IsolationLevel.Unspecified: - case IsolationLevel.ReadCommitted: - case IsolationLevel.ReadUncommitted: - case IsolationLevel.RepeatableRead: - case IsolationLevel.Serializable: - case IsolationLevel.Snapshot: - Debug.Fail("valid IsolationLevel " + value.ToString()); - break; - case IsolationLevel.Chaos: - break; - default: - Debug.Fail("invalid IsolationLevel " + value.ToString()); - break; + switch (value) + { + case IsolationLevel.Unspecified: + case IsolationLevel.ReadCommitted: + case IsolationLevel.ReadUncommitted: + case IsolationLevel.RepeatableRead: + case IsolationLevel.Serializable: + case IsolationLevel.Snapshot: + Debug.Fail("valid IsolationLevel " + value.ToString()); + break; + case IsolationLevel.Chaos: + break; + default: + Debug.Fail("invalid IsolationLevel " + value.ToString()); + break; } #endif return NotSupportedEnumerationValue(typeof(IsolationLevel), (int)value); @@ -1763,6 +1772,20 @@ static internal Exception ColumnEncryptionKeysNotFound() return ADP.Argument(StringsHelper.GetString(Strings.TCE_ColumnEncryptionKeysNotFound)); } + internal static SqlException AttestationFailed(string errorMessage, Exception innerException = null) + { + SqlErrorCollection errors = new(); + errors.Add(new SqlError( + infoNumber: 0, + errorState: 0, + errorClass: 0, + server: null, + errorMessage, + procedure: string.Empty, + lineNumber: 0)); + return SqlException.CreateException(errors, serverVersion: string.Empty, Guid.Empty, innerException); + } + // // TCE - Errors when performing attestation // diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs deleted file mode 100644 index b30b57154e..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ /dev/null @@ -1,1183 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data; - -namespace Microsoft.Data.SqlClient -{ - /// Class of variables for the Tds connection. - /// - internal static class TdsEnums - { - - // internal tdsparser constants - - public const short SQL_SERVER_VERSION_SEVEN = 7; - - public const string SQL_PROVIDER_NAME = Common.DbConnectionStringDefaults.ApplicationName; - - public static readonly Decimal SQL_SMALL_MONEY_MIN = new Decimal(-214748.3648); - public static readonly Decimal SQL_SMALL_MONEY_MAX = new Decimal(214748.3647); - - // sql debugging constants, sdci is the structure passed in - public const string SDCI_MAPFILENAME = "SqlClientSSDebug"; - public const byte SDCI_MAX_MACHINENAME = 32; - public const byte SDCI_MAX_DLLNAME = 16; - public const byte SDCI_MAX_DATA = 255; - public const int SQLDEBUG_OFF = 0; - public const int SQLDEBUG_ON = 1; - public const int SQLDEBUG_CONTEXT = 2; - public const string SP_SDIDEBUG = "sp_sdidebug"; - public static readonly string[] SQLDEBUG_MODE_NAMES = new string[3] { - "off", - "on", - "context" - }; - - // HACK!!! - // Constant for SqlDbType.SmallVarBinary... store internal variable here instead of on - // SqlDbType so that it is not surfaced to the user!!! Related to dtc and the fact that - // the TransactionManager TDS stream is the only token left that uses VarBinarys instead of - // BigVarBinarys. - public const SqlDbType SmallVarBinary = (SqlDbType)(SqlDbType.Variant) + 1; - - // network protocol string constants - public const string TCP = "tcp"; - public const string NP = "np"; - public const string RPC = "rpc"; - public const string BV = "bv"; - public const string ADSP = "adsp"; - public const string SPX = "spx"; - public const string VIA = "via"; - public const string LPC = "lpc"; - - // network function string contants - public const string INIT_SSPI_PACKAGE = "InitSSPIPackage"; - public const string INIT_SESSION = "InitSession"; - public const string CONNECTION_GET_SVR_USER = "ConnectionGetSvrUser"; - public const string GEN_CLIENT_CONTEXT = "GenClientContext"; - - // tdsparser packet handling constants - public const byte SOFTFLUSH = 0; - public const byte HARDFLUSH = 1; - public const byte IGNORE = 2; - - // header constants - public const int HEADER_LEN = 8; - public const int HEADER_LEN_FIELD_OFFSET = 2; - public const int SPID_OFFSET = 4; - public const int SQL2005_HEADER_LEN = 12; //2005 headers also include a MARS session id - public const int MARS_ID_OFFSET = 8; - public const int HEADERTYPE_QNOTIFICATION = 1; - public const int HEADERTYPE_MARS = 2; - public const int HEADERTYPE_TRACE = 3; - - // other various constants - public const int SUCCEED = 1; - public const int FAIL = 0; - public const short TYPE_SIZE_LIMIT = 8000; - public const int MIN_PACKET_SIZE = 512; - // Login packet can be no greater than 4k until server sends us env-change - // increasing packet size. - public const int DEFAULT_LOGIN_PACKET_SIZE = 4096; - public const int MAX_PRELOGIN_PAYLOAD_LENGTH = 1024; - public const int MAX_PACKET_SIZE = 32768; - public const int MAX_SERVER_USER_NAME = 256; // obtained from luxor - - // Severity 0 - 10 indicates informational (non-error) messages - // Severity 11 - 16 indicates errors that can be corrected by user (syntax errors, etc...) - // Severity 17 - 19 indicates failure due to insufficient resources in the server - // (max locks exceeded, not enough memory, other internal server limits reached, etc..) - // Severity 20 - 25 Severe problems with the server, connection terminated. - public const byte MIN_ERROR_CLASS = 11; // webdata 100667: This should actually be 11 - public const byte MAX_USER_CORRECTABLE_ERROR_CLASS = 16; - public const byte FATAL_ERROR_CLASS = 20; - - // Message types - public const byte MT_SQL = 1; // SQL command batch - public const byte MT_LOGIN = 2; // Login message for pre-7.0 - public const byte MT_RPC = 3; // Remote procedure call - public const byte MT_TOKENS = 4; // Table response data stream - public const byte MT_BINARY = 5; // Unformatted binary response data (UNUSED) - public const byte MT_ATTN = 6; // Attention (break) signal - public const byte MT_BULK = 7; // Bulk load data - public const byte MT_FEDAUTH = 8; // Authentication token for federated authentication - public const byte MT_CLOSE = 9; // Close subchannel (UNUSED) - public const byte MT_ERROR = 10; // Protocol error detected - public const byte MT_ACK = 11; // Protocol acknowledgement (UNUSED) - public const byte MT_ECHO = 12; // Echo data (UNUSED) - public const byte MT_LOGOUT = 13; // Logout message (UNUSED) - public const byte MT_TRANS = 14; // Transaction Manager Interface - public const byte MT_OLEDB = 15; // ? (UNUSED) - public const byte MT_LOGIN7 = 16; // Login message for 7.0 or later - public const byte MT_SSPI = 17; // SSPI message - public const byte MT_PRELOGIN = 18; // Pre-login handshake - - // Message status bits - public const byte ST_EOM = 0x1; // Packet is end-of-message - public const byte ST_AACK = 0x2; // Packet acknowledges attention (server to client) - public const byte ST_IGNORE = 0x2; // Ignore this event (client to server) - public const byte ST_BATCH = 0x4; // Message is part of a batch. - public const byte ST_RESET_CONNECTION = 0x8; // Exec sp_reset_connection prior to processing message - public const byte ST_RESET_CONNECTION_PRESERVE_TRANSACTION = 0x10; // reset prior to processing, with preserving local tx - - // TDS control tokens - public const byte SQLCOLFMT = 0xa1; - public const byte SQLPROCID = 0x7c; - public const byte SQLCOLNAME = 0xa0; - public const byte SQLTABNAME = 0xa4; - public const byte SQLCOLINFO = 0xa5; - public const byte SQLALTNAME = 0xa7; - public const byte SQLALTFMT = 0xa8; - public const byte SQLERROR = 0xaa; - public const byte SQLINFO = 0xab; - public const byte SQLRETURNVALUE = 0xac; - public const byte SQLRETURNSTATUS = 0x79; - public const byte SQLRETURNTOK = 0xdb; - public const byte SQLALTCONTROL = 0xaf; - public const byte SQLROW = 0xd1; - public const byte SQLNBCROW = 0xd2; // same as ROW with null-bit-compression support - public const byte SQLALTROW = 0xd3; - public const byte SQLDONE = 0xfd; - public const byte SQLDONEPROC = 0xfe; - public const byte SQLDONEINPROC = 0xff; - public const byte SQLOFFSET = 0x78; - public const byte SQLORDER = 0xa9; - public const byte SQLDEBUG_CMD = 0x60; - public const byte SQLLOGINACK = 0xad; - public const byte SQLFEATUREEXTACK = 0xae; // TDS 7.4 - feature ack - public const byte SQLSESSIONSTATE = 0xe4; // TDS 7.4 - connection resiliency session state - public const byte SQLENVCHANGE = 0xe3; // Environment change notification - public const byte SQLSECLEVEL = 0xed; // Security level token ??? - public const byte SQLROWCRC = 0x39; // ROWCRC datastream??? - public const byte SQLCOLMETADATA = 0x81; // Column metadata including name - public const byte SQLALTMETADATA = 0x88; // Alt column metadata including name - public const byte SQLSSPI = 0xed; // SSPI data - public const byte SQLFEDAUTHINFO = 0xee; // Info for client to generate fed auth token - public const byte SQLRESCOLSRCS = 0xa2; - public const byte SQLDATACLASSIFICATION = 0xa3; - - // Environment change notification streams - // TYPE on TDS ENVCHANGE token stream (from sql\ntdbms\include\odsapi.h) - // - public const byte ENV_DATABASE = 1; // Database changed - public const byte ENV_LANG = 2; // Language changed - public const byte ENV_CHARSET = 3; // Character set changed - public const byte ENV_PACKETSIZE = 4; // Packet size changed - public const byte ENV_LOCALEID = 5; // Unicode data sorting locale id - public const byte ENV_COMPFLAGS = 6; // Unicode data sorting comparison flags - public const byte ENV_COLLATION = 7; // SQL Collation - // The following are environment change tokens valid for 2005 or later. - public const byte ENV_BEGINTRAN = 8; // Transaction began - public const byte ENV_COMMITTRAN = 9; // Transaction committed - public const byte ENV_ROLLBACKTRAN = 10; // Transaction rolled back - public const byte ENV_ENLISTDTC = 11; // Enlisted in Distributed Transaction - public const byte ENV_DEFECTDTC = 12; // Defected from Distributed Transaction - public const byte ENV_LOGSHIPNODE = 13; // Realtime Log shipping primary node - public const byte ENV_PROMOTETRANSACTION = 15; // Promote Transaction - public const byte ENV_TRANSACTIONMANAGERADDRESS = 16; // Transaction Manager Address - public const byte ENV_TRANSACTIONENDED = 17; // Transaction Ended - public const byte ENV_SPRESETCONNECTIONACK = 18; // SP_Reset_Connection ack - public const byte ENV_USERINSTANCE = 19; // User Instance - public const byte ENV_ROUTING = 20; // Routing (ROR) information - - // done status stream bit masks - public const int DONE_MORE = 0x0001; // more command results coming - public const int DONE_ERROR = 0x0002; // error in command batch - public const int DONE_INXACT = 0x0004; // transaction in progress - public const int DONE_PROC = 0x0008; // done from stored proc - public const int DONE_COUNT = 0x0010; // count in done info - public const int DONE_ATTN = 0x0020; // oob ack - public const int DONE_INPROC = 0x0040; // like DONE_PROC except proc had error - public const int DONE_RPCINBATCH = 0x0080; // Done from RPC in batch - public const int DONE_SRVERROR = 0x0100; // Severe error in which resultset should be discarded - public const int DONE_FMTSENT = 0x8000; // fmt message sent, done_inproc req'd - - // Feature Extension - public const byte FEATUREEXT_TERMINATOR = 0xFF; - public const byte FEATUREEXT_SRECOVERY = 0x01; - public const byte FEATUREEXT_FEDAUTH = 0x02; - // 0x03 is for x_eFeatureExtensionId_Rcs - public const byte FEATUREEXT_TCE = 0x04; - public const byte FEATUREEXT_GLOBALTRANSACTIONS = 0x05; - // 0x06 is for x_eFeatureExtensionId_LoginToken - // 0x07 is for x_eFeatureExtensionId_ClientSideTelemetry - public const byte FEATUREEXT_AZURESQLSUPPORT = 0x08; - public const byte FEATUREEXT_DATACLASSIFICATION = 0x09; - public const byte FEATUREEXT_UTF8SUPPORT = 0x0A; - public const byte FEATUREEXT_SQLDNSCACHING = 0x0B; - - [Flags] - public enum FeatureExtension : uint - { - None = 0, - SessionRecovery = 1 << (TdsEnums.FEATUREEXT_SRECOVERY - 1), - FedAuth = 1 << (TdsEnums.FEATUREEXT_FEDAUTH - 1), - Tce = 1 << (TdsEnums.FEATUREEXT_TCE - 1), - GlobalTransactions = 1 << (TdsEnums.FEATUREEXT_GLOBALTRANSACTIONS - 1), - AzureSQLSupport = 1 << (TdsEnums.FEATUREEXT_AZURESQLSUPPORT - 1), - DataClassification = 1 << (TdsEnums.FEATUREEXT_DATACLASSIFICATION - 1), - UTF8Support = 1 << (TdsEnums.FEATUREEXT_UTF8SUPPORT - 1), - SQLDNSCaching = 1 << (TdsEnums.FEATUREEXT_SQLDNSCACHING - 1) - } - - public const uint UTF8_IN_TDSCOLLATION = 0x4000000; - - public const byte FEDAUTHLIB_LIVEID = 0X00; - public const byte FEDAUTHLIB_SECURITYTOKEN = 0x01; - public const byte FEDAUTHLIB_MSAL = 0x02; - public const byte FEDAUTHLIB_RESERVED = 0X7F; - - public enum FedAuthLibrary : byte - { - LiveId = FEDAUTHLIB_LIVEID, - SecurityToken = FEDAUTHLIB_SECURITYTOKEN, - MSAL = FEDAUTHLIB_MSAL, - Default = FEDAUTHLIB_RESERVED - } - - public const byte MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYINTEGRATED = 0x02; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE = 0x03; - public const byte MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the closest we have - public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW = 0x03; // Using the Interactive byte as that is the closest we have - public const byte MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03; // Using the Interactive byte as that's supported for Identity based authentication - public const byte MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03; // Using the Interactive byte as that is the closest we have to non-password based authentication modes - - public enum ActiveDirectoryWorkflow : byte - { - Password = MSALWORKFLOW_ACTIVEDIRECTORYPASSWORD, - Integrated = MSALWORKFLOW_ACTIVEDIRECTORYINTEGRATED, - Interactive = MSALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE, - ServicePrincipal = MSALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL, - DeviceCodeFlow = MSALWORKFLOW_ACTIVEDIRECTORYDEVICECODEFLOW, - ManagedIdentity = MSALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY, - Default = MSALWORKFLOW_ACTIVEDIRECTORYDEFAULT, - } - - // The string used for username in the error message when Authentication = Active Directory Integrated with FedAuth is used, if authentication fails. - public const string NTAUTHORITYANONYMOUSLOGON = @"NT Authority\Anonymous Logon"; - - // Loginrec defines - public const byte MAX_LOG_NAME = 30; // TDS 4.2 login rec max name length - public const byte MAX_PROG_NAME = 10; // max length of loginrec progran name - public const byte SEC_COMP_LEN = 8; // length of security compartments - public const byte MAX_PK_LEN = 6; // max length of TDS packet size - public const byte MAX_NIC_SIZE = 6; // The size of a MAC or client address - public const byte SQLVARIANT_SIZE = 2; // size of the fixed portion of a sql variant (type, cbPropBytes) - public const byte VERSION_SIZE = 4; // size of the tds version (4 unsigned bytes) - public const int CLIENT_PROG_VER = 0x06000000; // Client interface version - public const int SQL2005_LOG_REC_FIXED_LEN = 0x5e; - // misc - public const int TEXT_TIME_STAMP_LEN = 8; - public const int COLLATION_INFO_LEN = 4; - - /* - public const byte INT4_LSB_HI = 0; // lsb is low byte (eg 68000) - // public const byte INT4_LSB_LO = 1; // lsb is low byte (eg VAX) - public const byte INT2_LSB_HI = 2; // lsb is low byte (eg 68000) - // public const byte INT2_LSB_LO = 3; // lsb is low byte (eg VAX) - public const byte FLT_IEEE_HI = 4; // lsb is low byte (eg 68000) - public const byte CHAR_ASCII = 6; // ASCII character set - public const byte TWO_I4_LSB_HI = 8; // lsb is low byte (eg 68000 - // public const byte TWO_I4_LSB_LO = 9; // lsb is low byte (eg VAX) - // public const byte FLT_IEEE_LO = 10; // lsb is low byte (eg MSDOS) - public const byte FLT4_IEEE_HI = 12; // IEEE 4-byte floating point -lsb is high byte - // public const byte FLT4_IEEE_LO = 13; // IEEE 4-byte floating point -lsb is low byte - public const byte TWO_I2_LSB_HI = 16; // lsb is high byte - // public const byte TWO_I2_LSB_LO = 17; // lsb is low byte - - public const byte LDEFSQL = 0; // server sends its default - public const byte LDEFUSER = 0; // regular old user - public const byte LINTEGRATED = 8; // integrated security login - */ - - /* Versioning scheme table: - - Client sends: - 0x70000000 -> 7.0 - 0x71000000 -> 2000 RTM - 0x71000001 -> 2000 SP1 - 0x72xx0002 -> 2005 RTM - - Server responds: - 0x07000000 -> 7.0 // Notice server response format is different for bwd compat - 0x07010000 -> 2000 RTM // Notice server response format is different for bwd compat - 0x71000001 -> 2000 SP1 - 0x72xx0002 -> 2005 RTM - */ - - // Pre 2000 SP1 versioning scheme: - public const int SQL70OR2000_MAJOR = 0x07; // The high byte (b3) is not sufficient to distinguish - public const int SQL70_INCREMENT = 0x00; // 7.0 and 2000 - public const int SQL2000_INCREMENT = 0x01; // So we need to look at the high-mid byte (b2) as well - public const int DEFAULT_MINOR = 0x0000; - - // 2000 SP1 and beyond versioning scheme: - - // Majors: - public const int SQL2000SP1_MAJOR = 0x71; // For 2000 SP1 and later the versioning schema changed and - public const int SQL2005_MAJOR = 0x72; // the high-byte is sufficient to distinguish later versions - public const int SQL2008_MAJOR = 0x73; - public const int SQL2012_MAJOR = 0x74; - - // Increments: - public const int SQL2000SP1_INCREMENT = 0x00; - public const int SQL2005_INCREMENT = 0x09; - public const int SQL2008_INCREMENT = 0x0b; - public const int SQL2012_INCREMENT = 0x00; - - // Minors: - public const int SQL2000SP1_MINOR = 0x0001; - public const int SQL2005_RTM_MINOR = 0x0002; - public const int SQL2008_MINOR = 0x0003; - public const int SQL2012_MINOR = 0x0004; - - public const int ORDER_68000 = 1; - public const int USE_DB_ON = 1; - public const int INIT_DB_FATAL = 1; - public const int SET_LANG_ON = 1; - public const int INIT_LANG_FATAL = 1; - public const int ODBC_ON = 1; - public const int SSPI_ON = 1; - public const int REPL_ON = 3; - - - // send the read-only intent to the server - public const int READONLY_INTENT_ON = 1; - - // Token masks - public const byte SQLLenMask = 0x30; // mask to check for length tokens - public const byte SQLFixedLen = 0x30; // Mask to check for fixed token - public const byte SQLVarLen = 0x20; // Value to check for variable length token - public const byte SQLZeroLen = 0x10; // Value to check for zero length token - public const byte SQLVarCnt = 0x00; // Value to check for variable count token - - // Token masks for COLINFO status - public const byte SQLDifferentName = 0x20; // column name different than select list name - public const byte SQLExpression = 0x4; // column was result of an expression - public const byte SQLKey = 0x8; // column is part of the key for the table - public const byte SQLHidden = 0x10; // column not part of select list but added because part of key - - // Token masks for COLMETADATA flags - // first byte - public const byte Nullable = 0x1; - public const byte Identity = 0x10; - public const byte Updatability = 0xb; // mask off bits 3 and 4 - // second byte - public const byte ClrFixedLen = 0x1; // Fixed length CLR type - public const byte IsColumnSet = 0x4; // Column is an XML representation of an aggregation of other columns - public const byte IsEncrypted = 0x8; // Column is encrypted using TCE - - // null values - public const uint VARLONGNULL = 0xffffffff; // null value for text and image types - public const int VARNULL = 0xffff; // null value for character and binary types - public const int MAXSIZE = 8000; // max size for any column - public const byte FIXEDNULL = 0; - public const UInt64 UDTNULL = 0xffffffffffffffff; - - // SQL Server Data Type Tokens. - public const int SQLVOID = 0x1f; - public const int SQLTEXT = 0x23; - public const int SQLVARBINARY = 0x25; - public const int SQLINTN = 0x26; - public const int SQLVARCHAR = 0x27; - public const int SQLBINARY = 0x2d; - public const int SQLIMAGE = 0x22; - public const int SQLCHAR = 0x2f; - public const int SQLINT1 = 0x30; - public const int SQLBIT = 0x32; - public const int SQLINT2 = 0x34; - public const int SQLINT4 = 0x38; - public const int SQLMONEY = 0x3c; - public const int SQLDATETIME = 0x3d; - public const int SQLFLT8 = 0x3e; - public const int SQLFLTN = 0x6d; - public const int SQLMONEYN = 0x6e; - public const int SQLDATETIMN = 0x6f; - public const int SQLFLT4 = 0x3b; - public const int SQLMONEY4 = 0x7a; - public const int SQLDATETIM4 = 0x3a; - public const int SQLDECIMALN = 0x6a; - public const int SQLNUMERICN = 0x6c; - public const int SQLUNIQUEID = 0x24; - public const int SQLBIGCHAR = 0xaf; - public const int SQLBIGVARCHAR = 0xa7; - public const int SQLBIGBINARY = 0xad; - public const int SQLBIGVARBINARY = 0xa5; - public const int SQLBITN = 0x68; - public const int SQLNCHAR = 0xef; - public const int SQLNVARCHAR = 0xe7; - public const int SQLNTEXT = 0x63; - public const int SQLUDT = 0xF0; - - // aggregate operator type TDS tokens, used by compute statements: - public const int AOPCNTB = 0x09; - public const int AOPSTDEV = 0x30; - public const int AOPSTDEVP = 0x31; - public const int AOPVAR = 0x32; - public const int AOPVARP = 0x33; - - public const int AOPCNT = 0x4b; - public const int AOPSUM = 0x4d; - public const int AOPAVG = 0x4f; - public const int AOPMIN = 0x51; - public const int AOPMAX = 0x52; - public const int AOPANY = 0x53; - public const int AOPNOOP = 0x56; - - // SQL Server user-defined type tokens we care about - public const int SQLTIMESTAMP = 0x50; - - public const int MAX_NUMERIC_LEN = 0x11; // 17 bytes of data for max numeric/decimal length - public const int DEFAULT_NUMERIC_PRECISION = 0x1D; // 29 is the default max numeric precision(Decimal.MaxValue) if not user set - public const int SQL70_DEFAULT_NUMERIC_PRECISION = 0x1C; // 28 is the default max numeric precision for 7.0 (Decimal.MaxValue doesn't work for 7.0) - public const int MAX_NUMERIC_PRECISION = 0x26; // 38 is max numeric precision; - public const byte UNKNOWN_PRECISION_SCALE = 0xff; // -1 is value for unknown precision or scale - - // The following datatypes are specific to 2000 (version 8) and later. - public const int SQLINT8 = 0x7f; - public const int SQLVARIANT = 0x62; - - // The following datatypes are specific to 2005 (version 9) or later - public const int SQLXMLTYPE = 0xf1; - public const int XMLUNICODEBOM = 0xfeff; - public static readonly byte[] XMLUNICODEBOMBYTES = { 0xff, 0xfe }; - - // The following datatypes are specific to 2008 (version 10) or later - public const int SQLTABLE = 0xf3; - public const int SQLDATE = 0x28; - public const int SQLTIME = 0x29; - public const int SQLDATETIME2 = 0x2a; - public const int SQLDATETIMEOFFSET = 0x2b; - - public const int DEFAULT_VARTIME_SCALE = 7; - - //Partially length prefixed datatypes constants. These apply to XMLTYPE, BIGVARCHRTYPE, - // NVARCHARTYPE, and BIGVARBINTYPE. Valid for 2005 or later - - public const ulong SQL_PLP_NULL = 0xffffffffffffffff; // Represents null value - public const ulong SQL_PLP_UNKNOWNLEN = 0xfffffffffffffffe; // Data coming in chunks, total length unknown - public const int SQL_PLP_CHUNK_TERMINATOR = 0x00000000; // Represents end of chunked data. - public const ushort SQL_USHORTVARMAXLEN = 0xffff; // Second ushort in TDS stream is this value if one of max types - - // TVPs require some new in-value control tokens: - public const byte TVP_ROWCOUNT_ESTIMATE = 0x12; - public const byte TVP_ROW_TOKEN = 0x01; - public const byte TVP_END_TOKEN = 0x00; - public const ushort TVP_NOMETADATA_TOKEN = 0xFFFF; - public const byte TVP_ORDER_UNIQUE_TOKEN = 0x10; - - // TvpColumnMetaData flags - public const int TVP_DEFAULT_COLUMN = 0x200; - - // TVP_ORDER_UNIQUE_TOKEN flags - public const byte TVP_ORDERASC_FLAG = 0x1; - public const byte TVP_ORDERDESC_FLAG = 0x2; - public const byte TVP_UNIQUE_FLAG = 0x4; - - public const bool Is68K = false; - public const bool TraceTDS = false; - - // RPC function names - public const string SP_EXECUTESQL = "sp_executesql"; // used against 7.0 servers - public const string SP_PREPEXEC = "sp_prepexec"; // used against 7.5 servers - - public const string SP_PREPARE = "sp_prepare"; // used against 7.0 servers - public const string SP_EXECUTE = "sp_execute"; - public const string SP_UNPREPARE = "sp_unprepare"; - public const string SP_PARAMS = "sp_procedure_params_rowset"; - public const string SP_PARAMS_MANAGED = "sp_procedure_params_managed"; - public const string SP_PARAMS_MGD10 = "sp_procedure_params_100_managed"; - - // RPC ProcID's - // NOTE: It is more efficient to call these procs using ProcID's instead of names - public const ushort RPC_PROCID_CURSOR = 1; - public const ushort RPC_PROCID_CURSOROPEN = 2; - public const ushort RPC_PROCID_CURSORPREPARE = 3; - public const ushort RPC_PROCID_CURSOREXECUTE = 4; - public const ushort RPC_PROCID_CURSORPREPEXEC = 5; - public const ushort RPC_PROCID_CURSORUNPREPARE = 6; - public const ushort RPC_PROCID_CURSORFETCH = 7; - public const ushort RPC_PROCID_CURSOROPTION = 8; - public const ushort RPC_PROCID_CURSORCLOSE = 9; - public const ushort RPC_PROCID_EXECUTESQL = 10; - public const ushort RPC_PROCID_PREPARE = 11; - public const ushort RPC_PROCID_EXECUTE = 12; - public const ushort RPC_PROCID_PREPEXEC = 13; - public const ushort RPC_PROCID_PREPEXECRPC = 14; - public const ushort RPC_PROCID_UNPREPARE = 15; - - // For Transactions - public const string TRANS_BEGIN = "BEGIN TRANSACTION"; - public const string TRANS_COMMIT = "COMMIT TRANSACTION"; - public const string TRANS_ROLLBACK = "ROLLBACK TRANSACTION"; - public const string TRANS_IF_ROLLBACK = "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"; - public const string TRANS_SAVE = "SAVE TRANSACTION"; - - // For Transactions - isolation levels - public const string TRANS_READ_COMMITTED = "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; - public const string TRANS_READ_UNCOMMITTED = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; - public const string TRANS_REPEATABLE_READ = "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"; - public const string TRANS_SERIALIZABLE = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"; - public const string TRANS_SNAPSHOT = "SET TRANSACTION ISOLATION LEVEL SNAPSHOT"; - - // Batch RPC flags - public const byte SQL2000_RPCBATCHFLAG = 0x80; - public const byte SQL2005_RPCBATCHFLAG = 0xFF; - - // RPC flags - public const byte RPC_RECOMPILE = 0x1; - public const byte RPC_NOMETADATA = 0x2; - - // RPC parameter class - public const byte RPC_PARAM_BYREF = 0x1; - public const byte RPC_PARAM_DEFAULT = 0x2; - public const byte RPC_PARAM_ENCRYPTED = 0x8; - - // SQL parameter list text - public const string PARAM_OUTPUT = "output"; - - // SQL Parameter constants - public const int MAX_PARAMETER_NAME_LENGTH = 128; - - // metadata options (added around an existing sql statement) - - // prefixes - public const string FMTONLY_ON = " SET FMTONLY ON;"; - public const string FMTONLY_OFF = " SET FMTONLY OFF;"; - // suffixes - public const string BROWSE_ON = " SET NO_BROWSETABLE ON;"; - public const string BROWSE_OFF = " SET NO_BROWSETABLE OFF;"; - - // generic table name - public const string TABLE = "Table"; - - public const int EXEC_THRESHOLD = 0x3; // if the number of commands we execute is > than this threshold, than do prep/exec/unprep instead - // of executesql. - - // dbnetlib error values - public const short TIMEOUT_EXPIRED = -2; - public const short ENCRYPTION_NOT_SUPPORTED = 20; - public const short CTAIP_NOT_SUPPORTED = 21; - - // CAUTION: These are not error codes returned by SNI. This is used for backward compatibility - // since netlib (now removed from sqlclient) returned these codes. - - // SQL error values (from sqlerrorcodes.h) - public const int LOGON_FAILED = 18456; - public const int PASSWORD_EXPIRED = 18488; - public const int IMPERSONATION_FAILED = 1346; - public const int P_TOKENTOOLONG = 103; - - // SQL error that indicates retry for Always Encrypted - public const int TCE_CONVERSION_ERROR_CLIENT_RETRY = 33514; - public const int TCE_ENCLAVE_INVALID_SESSION_HANDLE = 33195; - - // SNI\Win32 error values - // NOTE: these are simply windows system error codes, not SNI specific - public const uint SNI_UNINITIALIZED = unchecked((uint)-1); - public const uint SNI_SUCCESS = 0; // The operation completed successfully. - public const uint SNI_WAIT_TIMEOUT = 258; // The wait operation timed out. - public const uint SNI_SUCCESS_IO_PENDING = 997; // Overlapped I/O operation is in progress. - - // Windows Sockets Error Codes - public const short SNI_WSAECONNRESET = 10054; // An existing connection was forcibly closed by the remote host. - - // SNI flags - public const UInt32 SNI_SSL_VALIDATE_CERTIFICATE = 1; // This enables validation of server certificate - public const UInt32 SNI_SSL_USE_SCHANNEL_CACHE = 2; // This enables schannel session cache - public const UInt32 SNI_SSL_IGNORE_CHANNEL_BINDINGS = 0x10; // Used with SSL Provider, sent to SNIAddProvider in case of SQL Authentication & Encrypt. - - public const string DEFAULT_ENGLISH_CODE_PAGE_STRING = "iso_1"; - public const short DEFAULT_ENGLISH_CODE_PAGE_VALUE = 1252; - public const short CHARSET_CODE_PAGE_OFFSET = 2; - internal const int MAX_SERVERNAME = 255; - - // Sql Statement Tokens in the DONE packet - // (see ntdbms\ntinc\tokens.h) - // - internal const ushort SELECT = 0xc1; - internal const ushort INSERT = 0xc3; - internal const ushort DELETE = 0xc4; - internal const ushort UPDATE = 0xc5; - internal const ushort ABORT = 0xd2; - internal const ushort BEGINXACT = 0xd4; - internal const ushort ENDXACT = 0xd5; - internal const ushort BULKINSERT = 0xf0; - internal const ushort OPENCURSOR = 0x20; - internal const ushort MERGE = 0x117; - - - // Login data validation Rules - // - internal const ushort MAXLEN_HOSTNAME = 128; // the client machine name - internal const ushort MAXLEN_CLIENTID = 128; - internal const ushort MAXLEN_CLIENTSECRET = 128; - internal const ushort MAXLEN_APPNAME = 128; // the client application name - internal const ushort MAXLEN_SERVERNAME = 128; // the server name - internal const ushort MAXLEN_CLIENTINTERFACE = 128; // the interface library name - internal const ushort MAXLEN_LANGUAGE = 128; // the initial language - internal const ushort MAXLEN_DATABASE = 128; // the initial database - internal const ushort MAXLEN_ATTACHDBFILE = 260; // the filename for a database that is to be attached during the connection process - internal const ushort MAXLEN_NEWPASSWORD = 128; // new password for the specified login. - - - // array copied directly from tdssort.h from luxor - public static readonly UInt16[] CODE_PAGE_FROM_SORT_ID = { - 0, /* 0 */ - 0, /* 1 */ - 0, /* 2 */ - 0, /* 3 */ - 0, /* 4 */ - 0, /* 5 */ - 0, /* 6 */ - 0, /* 7 */ - 0, /* 8 */ - 0, /* 9 */ - 0, /* 10 */ - 0, /* 11 */ - 0, /* 12 */ - 0, /* 13 */ - 0, /* 14 */ - 0, /* 15 */ - 0, /* 16 */ - 0, /* 17 */ - 0, /* 18 */ - 0, /* 19 */ - 0, /* 20 */ - 0, /* 21 */ - 0, /* 22 */ - 0, /* 23 */ - 0, /* 24 */ - 0, /* 25 */ - 0, /* 26 */ - 0, /* 27 */ - 0, /* 28 */ - 0, /* 29 */ - 437, /* 30 */ - 437, /* 31 */ - 437, /* 32 */ - 437, /* 33 */ - 437, /* 34 */ - 0, /* 35 */ - 0, /* 36 */ - 0, /* 37 */ - 0, /* 38 */ - 0, /* 39 */ - 850, /* 40 */ - 850, /* 41 */ - 850, /* 42 */ - 850, /* 43 */ - 850, /* 44 */ - 0, /* 45 */ - 0, /* 46 */ - 0, /* 47 */ - 0, /* 48 */ - 850, /* 49 */ - 1252, /* 50 */ - 1252, /* 51 */ - 1252, /* 52 */ - 1252, /* 53 */ - 1252, /* 54 */ - 850, /* 55 */ - 850, /* 56 */ - 850, /* 57 */ - 850, /* 58 */ - 850, /* 59 */ - 850, /* 60 */ - 850, /* 61 */ - 0, /* 62 */ - 0, /* 63 */ - 0, /* 64 */ - 0, /* 65 */ - 0, /* 66 */ - 0, /* 67 */ - 0, /* 68 */ - 0, /* 69 */ - 0, /* 70 */ - 1252, /* 71 */ - 1252, /* 72 */ - 1252, /* 73 */ - 1252, /* 74 */ - 1252, /* 75 */ - 0, /* 76 */ - 0, /* 77 */ - 0, /* 78 */ - 0, /* 79 */ - 1250, /* 80 */ - 1250, /* 81 */ - 1250, /* 82 */ - 1250, /* 83 */ - 1250, /* 84 */ - 1250, /* 85 */ - 1250, /* 86 */ - 1250, /* 87 */ - 1250, /* 88 */ - 1250, /* 89 */ - 1250, /* 90 */ - 1250, /* 91 */ - 1250, /* 92 */ - 1250, /* 93 */ - 1250, /* 94 */ - 1250, /* 95 */ - 1250, /* 96 */ - 1250, /* 97 */ - 1250, /* 98 */ - 0, /* 99 */ - 0, /* 100 */ - 0, /* 101 */ - 0, /* 102 */ - 0, /* 103 */ - 1251, /* 104 */ - 1251, /* 105 */ - 1251, /* 106 */ - 1251, /* 107 */ - 1251, /* 108 */ - 0, /* 109 */ - 0, /* 110 */ - 0, /* 111 */ - 1253, /* 112 */ - 1253, /* 113 */ - 1253, /* 114 */ - 0, /* 115 */ - 0, /* 116 */ - 0, /* 117 */ - 0, /* 118 */ - 0, /* 119 */ - 1253, /* 120 */ - 1253, /* 121 */ - 1253, /* 122 */ - 0, /* 123 */ - 1253, /* 124 */ - 0, /* 125 */ - 0, /* 126 */ - 0, /* 127 */ - 1254, /* 128 */ - 1254, /* 129 */ - 1254, /* 130 */ - 0, /* 131 */ - 0, /* 132 */ - 0, /* 133 */ - 0, /* 134 */ - 0, /* 135 */ - 1255, /* 136 */ - 1255, /* 137 */ - 1255, /* 138 */ - 0, /* 139 */ - 0, /* 140 */ - 0, /* 141 */ - 0, /* 142 */ - 0, /* 143 */ - 1256, /* 144 */ - 1256, /* 145 */ - 1256, /* 146 */ - 0, /* 147 */ - 0, /* 148 */ - 0, /* 149 */ - 0, /* 150 */ - 0, /* 151 */ - 1257, /* 152 */ - 1257, /* 153 */ - 1257, /* 154 */ - 1257, /* 155 */ - 1257, /* 156 */ - 1257, /* 157 */ - 1257, /* 158 */ - 1257, /* 159 */ - 1257, /* 160 */ - 0, /* 161 */ - 0, /* 162 */ - 0, /* 163 */ - 0, /* 164 */ - 0, /* 165 */ - 0, /* 166 */ - 0, /* 167 */ - 0, /* 168 */ - 0, /* 169 */ - 0, /* 170 */ - 0, /* 171 */ - 0, /* 172 */ - 0, /* 173 */ - 0, /* 174 */ - 0, /* 175 */ - 0, /* 176 */ - 0, /* 177 */ - 0, /* 178 */ - 0, /* 179 */ - 0, /* 180 */ - 0, /* 181 */ - 0, /* 182 */ - 1252, /* 183 */ - 1252, /* 184 */ - 1252, /* 185 */ - 1252, /* 186 */ - 0, /* 187 */ - 0, /* 188 */ - 0, /* 189 */ - 0, /* 190 */ - 0, /* 191 */ - 932, /* 192 */ - 932, /* 193 */ - 949, /* 194 */ - 949, /* 195 */ - 950, /* 196 */ - 950, /* 197 */ - 936, /* 198 */ - 936, /* 199 */ - 932, /* 200 */ - 949, /* 201 */ - 950, /* 202 */ - 936, /* 203 */ - 874, /* 204 */ - 874, /* 205 */ - 874, /* 206 */ - 0, /* 207 */ - 0, /* 208 */ - 0, /* 209 */ - 1252, /* 210 */ - 1252, /* 211 */ - 1252, /* 212 */ - 1252, /* 213 */ - 1252, /* 214 */ - 1252, /* 215 */ - 1252, /* 216 */ - 1252, /* 217 */ - 0, /* 218 */ - 0, /* 219 */ - 0, /* 220 */ - 0, /* 221 */ - 0, /* 222 */ - 0, /* 223 */ - 0, /* 224 */ - 0, /* 225 */ - 0, /* 226 */ - 0, /* 227 */ - 0, /* 228 */ - 0, /* 229 */ - 0, /* 230 */ - 0, /* 231 */ - 0, /* 232 */ - 0, /* 233 */ - 0, /* 234 */ - 0, /* 235 */ - 0, /* 236 */ - 0, /* 237 */ - 0, /* 238 */ - 0, /* 239 */ - 0, /* 240 */ - 0, /* 241 */ - 0, /* 242 */ - 0, /* 243 */ - 0, /* 244 */ - 0, /* 245 */ - 0, /* 246 */ - 0, /* 247 */ - 0, /* 248 */ - 0, /* 249 */ - 0, /* 250 */ - 0, /* 251 */ - 0, /* 252 */ - 0, /* 253 */ - 0, /* 254 */ - 0, /* 255 */ - }; - - internal enum UDTFormatType - { - Native = 1, - UserDefined = 2 - } - - internal enum TransactionManagerRequestType - { - GetDTCAddress = 0, - Propagate = 1, - Begin = 5, - Promote = 6, - Commit = 7, - Rollback = 8, - Save = 9 - }; - - internal enum TransactionManagerIsolationLevel - { - Unspecified = 0x00, - ReadUncommitted = 0x01, - ReadCommitted = 0x02, - RepeatableRead = 0x03, - Serializable = 0x04, - Snapshot = 0x05 - } - - internal enum GenericType - { - MultiSet = 131, - }; - - // Date, Time, DateTime2, DateTimeOffset specific constants - internal static readonly Int64[] TICKS_FROM_SCALE = { - 10000000, - 1000000, - 100000, - 10000, - 1000, - 100, - 10, - 1, - }; - - internal const int MAX_TIME_SCALE = 7; // Maximum scale for time-related types - internal const int MAX_TIME_LENGTH = 5; // Maximum length for time - internal const int MAX_DATETIME2_LENGTH = 8; // Maximum length for datetime2 - internal const int WHIDBEY_DATE_LENGTH = 10; - internal static readonly int[] WHIDBEY_TIME_LENGTH = { 8, 10, 11, 12, 13, 14, 15, 16 }; - internal static readonly int[] WHIDBEY_DATETIME2_LENGTH = { 19, 21, 22, 23, 24, 25, 26, 27 }; - internal static readonly int[] WHIDBEY_DATETIMEOFFSET_LENGTH = { 26, 28, 29, 30, 31, 32, 33, 34 }; - - internal enum FedAuthInfoId : byte - { - Stsurl = 0x01, // FedAuthInfoData is token endpoint URL from which to acquire fed auth token - Spn = 0x02, // FedAuthInfoData is the SPN to use for acquiring fed auth token - } - - // Data Classification constants - internal const byte DATA_CLASSIFICATION_NOT_ENABLED = 0x00; - internal const byte DATA_CLASSIFICATION_VERSION_WITHOUT_RANK_SUPPORT = 0x01; - internal const byte DATA_CLASSIFICATION_VERSION_MAX_SUPPORTED = 0x02; - - // TCE Related constants - internal const byte MAX_SUPPORTED_TCE_VERSION = 0x03; // max version - internal const byte MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT = 0x02; // min version with enclave support - internal const ushort MAX_TCE_CIPHERINFO_SIZE = 2048; // max size of cipherinfo blob - internal const long MAX_TCE_CIPHERTEXT_SIZE = 2147483648; // max size of encrypted blob- currently 2GB. - internal const byte CustomCipherAlgorithmId = 0; // Id used for custom encryption algorithm. - - internal const int AEAD_AES_256_CBC_HMAC_SHA256 = 2; - internal const string ENCLAVE_TYPE_VBS = "VBS"; - internal const string ENCLAVE_TYPE_SGX = "SGX"; -#if ENCLAVE_SIMULATOR - internal const string ENCLAVE_TYPE_SIMULATOR = "SIMULATOR"; -#endif - // TCE Param names for exec handling - internal const string TCE_PARAM_CIPHERTEXT = "cipherText"; - internal const string TCE_PARAM_CIPHER_ALGORITHM_ID = "cipherAlgorithmId"; - internal const string TCE_PARAM_COLUMNENCRYPTION_KEY = "columnEncryptionKey"; - internal const string TCE_PARAM_ENCRYPTION_ALGORITHM = "encryptionAlgorithm"; - internal const string TCE_PARAM_ENCRYPTIONTYPE = "encryptionType"; - internal const string TCE_PARAM_ENCRYPTIONKEY = "encryptionKey"; - internal const string TCE_PARAM_MASTERKEY_PATH = "masterKeyPath"; - internal const string TCE_PARAM_ENCRYPTED_CEK = "encryptedColumnEncryptionKey"; - internal const string TCE_PARAM_CLIENT_KEYSTORE_PROVIDERS = "clientKeyStoreProviders"; - internal const string TCE_PARAM_FORCE_COLUMN_ENCRYPTION = "ForceColumnEncryption(true)"; - } - - internal enum ParsingErrorState - { - Undefined = 0, - FedAuthInfoLengthTooShortForCountOfInfoIds = 1, - FedAuthInfoLengthTooShortForData = 2, - FedAuthInfoFailedToReadCountOfInfoIds = 3, - FedAuthInfoFailedToReadTokenStream = 4, - FedAuthInfoInvalidOffset = 5, - FedAuthInfoFailedToReadData = 6, - FedAuthInfoDataNotUnicode = 7, - FedAuthInfoDoesNotContainStsurlAndSpn = 8, - FedAuthInfoNotReceived = 9, - FedAuthNotAcknowledged = 10, - FedAuthFeatureAckContainsExtraData = 11, - FedAuthFeatureAckUnknownLibraryType = 12, - UnrequestedFeatureAckReceived = 13, - UnknownFeatureAck = 14, - InvalidTdsTokenReceived = 15, - SessionStateLengthTooShort = 16, - SessionStateInvalidStatus = 17, - CorruptedTdsStream = 18, - ProcessSniPacketFailed = 19, - FedAuthRequiredPreLoginResponseInvalidValue = 20, - TceUnknownVersion = 21, - TceInvalidVersion = 22, - TceInvalidOrdinalIntoCipherInfoTable = 23, - DataClassificationInvalidVersion = 24, - DataClassificationNotExpected = 25, - DataClassificationInvalidLabelIndex = 26, - DataClassificationInvalidInformationTypeIndex = 27 - } - - internal enum SniContext - { - Undefined = 0, - Snix_Connect, - Snix_PreLoginBeforeSuccessfullWrite, - Snix_PreLogin, - Snix_LoginSspi, - Snix_ProcessSspi, - Snix_Login, - Snix_EnableMars, - Snix_AutoEnlist, - Snix_GetMarsSession, - Snix_Execute, - Snix_Read, - Snix_Close, - Snix_SendRows, - } - - /// - public enum SqlConnectionColumnEncryptionSetting - { - /// - Disabled = 0, - - /// - Enabled, - } - - /// - [Flags] - public enum SqlConnectionOverrides - { - /// - None = 0, - /// - OpenWithoutRetry = 1, - } - - /// - public enum SqlCommandColumnEncryptionSetting - { - /// - UseConnectionSetting = 0, - - /// - Enabled, - - /// - ResultSetOnly, - - /// - Disabled, - } - - /// - public enum SqlConnectionAttestationProtocol - { - /// - NotSpecified = 0, - - /// - AAS = 1, - -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif - - /// - HGS = 3 - } - - /// - public enum SqlConnectionIPAddressPreference - { - /// - IPv4First = 0, // default - - /// - IPv6First = 1, - - /// - UsePlatformDefault = 2 - } - - /// - public enum SqlAuthenticationMethod - { - /// - NotSpecified = 0, - - /// - SqlPassword, - - /// - ActiveDirectoryPassword, - - /// - ActiveDirectoryIntegrated, - - /// - ActiveDirectoryInteractive, - - /// - ActiveDirectoryServicePrincipal, - - /// - ActiveDirectoryDeviceCodeFlow, - - /// - ActiveDirectoryManagedIdentity, - - /// - ActiveDirectoryMSI, - - /// - ActiveDirectoryDefault, -#if ADONET_CERT_AUTH - SqlCertificate -#endif - } - // This enum indicates the state of TransparentNetworkIPResolution - // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. - internal enum TransparentNetworkResolutionState - { - DisabledMode = 0, - SequentialMode, - ParallelMode - }; - - internal class ActiveDirectoryAuthentication - { - internal const string AdoClientId = "2fd908ad-0664-4344-b9be-cd3e8b574c38"; - internal const string MSALGetAccessTokenFunctionName = "AcquireToken"; - } - - // Fields in the first resultset of "sp_describe_parameter_encryption". - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet1 - { - KeyOrdinal = 0, - DbId, - KeyId, - KeyVersion, - KeyMdVersion, - EncryptedKey, - ProviderName, - KeyPath, - KeyEncryptionAlgorithm, - IsRequestedByEnclave, - KeySignature, - } - - // Fields in the second resultset of "sp_describe_parameter_encryption" - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet2 - { - ParameterOrdinal = 0, - ParameterName, - ColumnEncryptionAlgorithm, - ColumnEncryptionType, - ColumnEncryptionKeyOrdinal, - NormalizationRuleVersion, - } - - // Fields in the third resultset of "sp_describe_parameter_encryption". - // We expect the server to return the fields in the resultset in the same order as mentioned below. - // If the server changes the below order, then transparent parameter encryption will break. - internal enum DescribeParameterEncryptionResultSet3 - { - AttestationInfo = 0, - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 4e07e27374..cd0fab3e2d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers; using System.Collections.Generic; using System.Data; using System.Data.SqlTypes; @@ -22,6 +23,7 @@ using Microsoft.Data.SqlClient.DataClassification; using Microsoft.Data.SqlClient.Server; using Microsoft.Data.SqlTypes; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient { @@ -215,6 +217,8 @@ internal static void Assert(string message) private bool _is2012 = false; + private bool _is2022 = false; + private byte[] _sniSpnBuffer = null; // UNDONE - need to have some for both instances - both command and default??? @@ -294,7 +298,7 @@ internal bool IsColumnEncryptionSupported internal bool isTcpProtocol { get; set; } - internal string FQDNforDNSCahce { get; set; } + internal string FQDNforDNSCache { get; set; } /// /// Get if data classification is enabled by the server. @@ -492,18 +496,21 @@ internal void Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, - bool encrypt, - bool trustServerCert, - bool integratedSecurity, + SqlConnectionString connectionOptions, bool withFailover, bool isFirstTransparentAttempt, - SqlAuthenticationMethod authType, - string certificate, ServerCertificateValidationCallback serverCallback, ClientCertificateRetrievalCallback clientCallback, bool useOriginalAddressInfo, bool disableTnir) { + SqlConnectionEncryptOption encrypt = connectionOptions.Encrypt; + bool trustServerCert = connectionOptions.TrustServerCertificate; + bool integratedSecurity = connectionOptions.IntegratedSecurity; + SqlAuthenticationMethod authType = connectionOptions.Authentication; + string certificate = connectionOptions.Certificate; + string hostNameInCertificate = connectionOptions.HostNameInCertificate; + if (_state != TdsParserState.Closed) { Debug.Fail("TdsParser.Connect called on non-closed connection!"); @@ -529,10 +536,10 @@ internal void Connect(ServerInfo serverInfo, if (connHandler.ConnectionOptions.LocalDBInstance != null) { LocalDBAPI.CreateLocalDBInstance(connHandler.ConnectionOptions.LocalDBInstance); - if (encrypt) + if (encrypt == SqlConnectionEncryptOption.Mandatory) { // Encryption is not supported on SQL Local DB - disable it for current session. - encrypt = false; + encrypt = SqlConnectionEncryptOption.Optional; SqlClientEventSource.Log.TryTraceEvent(" Encryption will be disabled as target server is a SQL Local DB instance."); } } @@ -541,8 +548,19 @@ internal void Connect(ServerInfo serverInfo, if (integratedSecurity || authType == SqlAuthenticationMethod.ActiveDirectoryIntegrated) { LoadSSPILibrary(); - // now allocate proper length of buffer - _sniSpnBuffer = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength]; + if (!string.IsNullOrEmpty(serverInfo.ServerSPN)) + { + // Native SNI requires the Unicode encoding and any other encoding like UTF8 breaks the code. + byte[] srvSPN = Encoding.Unicode.GetBytes(serverInfo.ServerSPN); + Trace.Assert(srvSPN.Length <= SNINativeMethodWrapper.SniMaxComposedSpnLength, "The provided SPN length exceeded the buffer size."); + _sniSpnBuffer = srvSPN; + SqlClientEventSource.Log.TryTraceEvent(" Server SPN `{0}` from the connection string is used.", serverInfo.ServerSPN); + } + else + { + // now allocate proper length of buffer + _sniSpnBuffer = new byte[SNINativeMethodWrapper.SniMaxComposedSpnLength]; + } SqlClientEventSource.Log.TryTraceEvent(" SSPI or Active Directory Authentication Library for SQL Server based integrated authentication"); } else @@ -583,6 +601,12 @@ internal void Connect(ServerInfo serverInfo, } } + // if Strict encryption is chosen trust server certificate should always be false. + if (encrypt == SqlConnectionEncryptOption.Strict) + { + trustServerCert = false; + } + byte[] instanceName = null; Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); @@ -604,16 +628,17 @@ internal void Connect(ServerInfo serverInfo, int totalTimeout = _connHandler.ConnectionOptions.ConnectTimeout; - FQDNforDNSCahce = serverInfo.ResolvedServerName; + FQDNforDNSCache = serverInfo.ResolvedServerName; - int commaPos = FQDNforDNSCahce.IndexOf(","); + int commaPos = FQDNforDNSCache.IndexOf(","); if (commaPos != -1) { - FQDNforDNSCahce = FQDNforDNSCahce.Substring(0, commaPos); + FQDNforDNSCache = FQDNforDNSCache.Substring(0, commaPos); } - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, _sniSpnBuffer, false, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCahce); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, _sniSpnBuffer, + false, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, FQDNforDNSCache, + encrypt == SqlConnectionEncryptOption.Strict, hostNameInCertificate); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -642,7 +667,7 @@ internal void Connect(ServerInfo serverInfo, serverInfo.ResolvedServerName : serverInfo.PreRoutingServerName); } _state = TdsParserState.OpenNotLoggedIn; - _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; // SQL BU DT 376766 + _physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfulWrite; // SQL BU DT 376766 _physicalStateObj.TimeoutTime = timerExpire; bool marsCapable = false; @@ -654,12 +679,12 @@ internal void Connect(ServerInfo serverInfo, Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SniGetConnectionId"); // for DNS Caching phase 1 - AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); + AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache); - if(!ClientOSEncryptionSupport) + if (!ClientOSEncryptionSupport) { //If encryption is required, an error will be thrown. - if (encrypt) + if (encrypt != SqlConnectionEncryptOption.Optional) { _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); @@ -670,7 +695,8 @@ internal void Connect(ServerInfo serverInfo, // UNDONE - send "" for instance now, need to fix later SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); - SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, !string.IsNullOrEmpty(certificate), + useOriginalAddressInfo, serverCallback, clientCallback); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.SendPreLoginHandshake); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ConsumePreLoginHandshake); @@ -678,8 +704,8 @@ internal void Connect(ServerInfo serverInfo, _physicalStateObj.SniContext = SniContext.Snix_PreLogin; SqlClientEventSource.Log.TryTraceEvent(" Consuming prelogin handshake"); - PreLoginHandshakeStatus status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, - out marsCapable, out _connHandler._fedAuthRequired); + PreLoginHandshakeStatus status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, + clientCallback, out marsCapable, out _connHandler._fedAuthRequired, encrypt == SqlConnectionEncryptOption.Strict); if (status == PreLoginHandshakeStatus.InstanceFailure) { @@ -688,8 +714,9 @@ internal void Connect(ServerInfo serverInfo, // On Instance failure re-connect and flush SNI named instance cache. _physicalStateObj.SniContext = SniContext.Snix_Connect; - _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, - out instanceName, _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, serverInfo.ResolvedServerName); + _physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, + _sniSpnBuffer, true, true, fParallel, transparentNetworkResolutionState, totalTimeout, _connHandler.ConnectionOptions.IPAddressPreference, + serverInfo.ResolvedServerName, encrypt == SqlConnectionEncryptOption.Strict, hostNameInCertificate); if (TdsEnums.SNI_SUCCESS != _physicalStateObj.Status) { @@ -704,11 +731,12 @@ internal void Connect(ServerInfo serverInfo, SqlClientEventSource.Log.TryTraceEvent(" Sending prelogin handshake"); // for DNS Caching phase 1 - AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCahce); + AssignPendingDNSInfo(serverInfo.UserProtocol, FQDNforDNSCache); - SendPreLoginHandshake(instanceName, encrypt, !string.IsNullOrEmpty(certificate), useOriginalAddressInfo); - status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, - out marsCapable, out _connHandler._fedAuthRequired); + SendPreLoginHandshake(instanceName, encrypt, integratedSecurity, !string.IsNullOrEmpty(certificate), + useOriginalAddressInfo, serverCallback, clientCallback); + status = ConsumePreLoginHandshake(authType, encrypt, trustServerCert, integratedSecurity, serverCallback, clientCallback, + out marsCapable, out _connHandler._fedAuthRequired, encrypt == SqlConnectionEncryptOption.Strict); // Don't need to check for 7.0 failure, since we've already consumed // one pre-login packet and know we are connecting to 2000. @@ -951,8 +979,27 @@ internal void BestEffortCleanup() } } - private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clientCertificate, bool useCtaip) + private void SendPreLoginHandshake( + byte[] instanceName, + SqlConnectionEncryptOption encrypt, + bool integratedSecurity, + bool clientCertificate, + bool useCtaip, + ServerCertificateValidationCallback serverCallback, + ClientCertificateRetrievalCallback clientCallback) { + if (encrypt == SqlConnectionEncryptOption.Strict) + { + //Always validate the certificate when in strict encryption mode + uint info = TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE | TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE | TdsEnums.SNI_SSL_SEND_ALPN_EXTENSION; + + EnableSsl(info, encrypt, integratedSecurity, serverCallback, clientCallback); + + // Since encryption has already been negotiated, we need to set encryption not supported in + // prelogin so that we don't try to negotiate encryption again during ConsumePreLoginHandshake. + _encryptionOption = EncryptionOptions.NOT_SUP; + } + // PreLoginHandshake buffer consists of: // 1) Standard header, with type = MT_PRELOGIN // 2) Consecutive 5 bytes for each option, (1 byte length, 2 byte offset, 2 byte payload length) @@ -1013,7 +1060,7 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien else { // Else, inform server of user request. - if (encrypt) + if (encrypt == SqlConnectionEncryptOption.Mandatory) { payload[payloadLength] = (byte)EncryptionOptions.ON; _encryptionOption = EncryptionOptions.ON; @@ -1128,10 +1175,119 @@ private void SendPreLoginHandshake(byte[] instanceName, bool encrypt, bool clien _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); } - private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod authType, bool encrypt, bool trustServerCert, bool integratedSecurity, ServerCertificateValidationCallback serverCallback, - ClientCertificateRetrievalCallback clientCallback, out bool marsCapable, out bool fedAuthRequired) + private void EnableSsl(uint info, SqlConnectionEncryptOption encrypt, bool integratedSecurity, ServerCertificateValidationCallback serverCallback, ClientCertificateRetrievalCallback clientCallback) { + uint error = 0; + + if (encrypt && !integratedSecurity) + { + // optimization: in case of SQL Authentication and encryption in TDS, set SNI_SSL_IGNORE_CHANNEL_BINDINGS + // to let SNI know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. + // This applies to Native SNI + info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; + } + + // Add SSL (Encryption) SNI provider. + SNINativeMethodWrapper.AuthProviderInfo authInfo = new SNINativeMethodWrapper.AuthProviderInfo(); + authInfo.flags = info; + authInfo.tlsFirst = encrypt == SqlConnectionEncryptOption.Strict; + authInfo.certId = null; + authInfo.certHash = false; + authInfo.clientCertificateCallbackContext = IntPtr.Zero; + authInfo.clientCertificateCallback = null; + authInfo.serverCertFileName = null; + + if ((_encryptionOption & EncryptionOptions.CLIENT_CERT) != 0) + { + + string certificate = _connHandler.ConnectionOptions.Certificate; + + if (certificate.StartsWith("subject:", StringComparison.OrdinalIgnoreCase)) + { + authInfo.certId = certificate.Substring(8); + } + else if (certificate.StartsWith("sha1:", StringComparison.OrdinalIgnoreCase)) + { + authInfo.certId = certificate.Substring(5); + authInfo.certHash = true; + } + + if (clientCallback != null) + { + authInfo.clientCertificateCallbackContext = clientCallback; + authInfo.clientCertificateCallback = _clientCertificateCallback; + } + } + + error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, authInfo); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + // in the case where an async connection is made, encryption is used and Windows Authentication is used, + // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its + // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete + // before calling SNISecGenClientContext). + error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining(), out uint protocolVersion); + + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + string warningMessage = SslProtocolsHelper.GetProtocolWarning(protocolVersion); + if (!string.IsNullOrEmpty(warningMessage)) + { + if (!encrypt && LocalAppContextSwitches.SuppressInsecureTLSWarning) + { + // Skip console warning + SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(EnableSsl), SqlClientLogger.LogLevel.Warning, warningMessage); + } + else + { + // This logs console warning of insecure protocol in use. + _logger.LogWarning(nameof(TdsParser), nameof(EnableSsl), warningMessage); + } + } + + // Validate server certificate + if (serverCallback != null) + { + X509Certificate2 serverCert = null; + + error = SNINativeMethodWrapper.SNISecGetServerCertificate(_physicalStateObj.Handle, ref serverCert); + if (error != TdsEnums.SNI_SUCCESS) + { + _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); + ThrowExceptionAndWarning(_physicalStateObj); + } + + bool valid = serverCallback(serverCert); + if (!valid) + { + throw SQL.InvalidServerCertificate(); + } + } + + // create a new packet encryption changes the internal packet size Bug# 228403 + _physicalStateObj.ClearAllWritePackets(); + } + + private PreLoginHandshakeStatus ConsumePreLoginHandshake( + SqlAuthenticationMethod authType, + SqlConnectionEncryptOption encrypt, + bool trustServerCert, + bool integratedSecurity, + ServerCertificateValidationCallback serverCallback, + ClientCertificateRetrievalCallback clientCallback, + out bool marsCapable, + out bool fedAuthRequired, + bool tlsFirst) + { // Assign default values marsCapable = _fMARS; fedAuthRequired = false; @@ -1161,7 +1317,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod byte[] payload = new byte[_physicalStateObj._inBytesPacket]; Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - result = _physicalStateObj.TryReadByteArray(payload, 0, payload.Length); + result = _physicalStateObj.TryReadByteArray(payload, payload.Length); if (!result) { throw SQL.SynchronousCallMayNotPend(); } @@ -1237,7 +1393,7 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod break; case (EncryptionOptions.NOT_SUP): - if ((serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ) + if (!tlsFirst && (serverOption & EncryptionOptions.OPTIONS_MASK) == EncryptionOptions.REQ) { _physicalStateObj.AddError(new SqlError(TdsEnums.ENCRYPTION_NOT_SUPPORTED, (byte)0x00, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.EncryptionNotSupportedByClient(), "", 0)); _physicalStateObj.Dispose(); @@ -1270,112 +1426,13 @@ private PreLoginHandshakeStatus ConsumePreLoginHandshake(SqlAuthenticationMethod trustServerCert = true; } - UInt32 error = 0; - // Validate Certificate if Trust Server Certificate=false and Encryption forced (EncryptionOptions.ON) from Server. bool shouldValidateServerCert = (_encryptionOption == EncryptionOptions.ON && !trustServerCert) || ((authType != SqlAuthenticationMethod.NotSpecified || _connHandler._accessTokenInBytes != null) && !trustServerCert); UInt32 info = (shouldValidateServerCert ? TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE : 0) | (is2005OrLater && (_encryptionOption & EncryptionOptions.CLIENT_CERT) == 0 ? TdsEnums.SNI_SSL_USE_SCHANNEL_CACHE : 0); - if (encrypt && !integratedSecurity) - { - // optimization: in case of SQL Authentication and encryption, set SNI_SSL_IGNORE_CHANNEL_BINDINGS to let SNI - // know that it does not need to allocate/retrieve the Channel Bindings from the SSL context. - info |= TdsEnums.SNI_SSL_IGNORE_CHANNEL_BINDINGS; - } - - // Add SSL (Encryption) SNI provider. - SNINativeMethodWrapper.AuthProviderInfo authInfo = new SNINativeMethodWrapper.AuthProviderInfo(); - authInfo.flags = info; - authInfo.certId = null; - authInfo.certHash = false; - authInfo.clientCertificateCallbackContext = IntPtr.Zero; - authInfo.clientCertificateCallback = null; - - if ((_encryptionOption & EncryptionOptions.CLIENT_CERT) != 0) - { - - string certificate = _connHandler.ConnectionOptions.Certificate; - - if (certificate.StartsWith("subject:", StringComparison.OrdinalIgnoreCase)) - { - authInfo.certId = certificate.Substring(8); - } - else if (certificate.StartsWith("sha1:", StringComparison.OrdinalIgnoreCase)) - { - authInfo.certId = certificate.Substring(5); - authInfo.certHash = true; - } - - if (clientCallback != null) - { - authInfo.clientCertificateCallbackContext = clientCallback; - authInfo.clientCertificateCallback = _clientCertificateCallback; - } - } - - error = SNINativeMethodWrapper.SNIAddProvider(_physicalStateObj.Handle, SNINativeMethodWrapper.ProviderEnum.SSL_PROV, authInfo); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - // in the case where an async connection is made, encryption is used and Windows Authentication is used, - // wait for SSL handshake to complete, so that the SSL context is fully negotiated before we try to use its - // Channel Bindings as part of the Windows Authentication context build (SSL handshake must complete - // before calling SNISecGenClientContext). - error = SNINativeMethodWrapper.SNIWaitForSSLHandshakeToComplete(_physicalStateObj.Handle, _physicalStateObj.GetTimeoutRemaining(), out uint protocolVersion); - - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - string warningMessage = SslProtocolsHelper.GetProtocolWarning(protocolVersion); - if (!string.IsNullOrEmpty(warningMessage)) - { - if (!encrypt && LocalAppContextSwitches.SuppressInsecureTLSWarning) - { - // Skip console warning - SqlClientEventSource.Log.TryTraceEvent("{3}", nameof(TdsParser), nameof(ConsumePreLoginHandshake), SqlClientLogger.LogLevel.Warning, warningMessage); - } - else - { - // This logs console warning of insecure protocol in use. - _logger.LogWarning(nameof(TdsParser), nameof(ConsumePreLoginHandshake), warningMessage); - } - } - - // Validate server certificate - if (serverCallback != null) - { - X509Certificate2 serverCert = null; - - error = SNINativeMethodWrapper.SNISecGetServerCertificate(_physicalStateObj.Handle, ref serverCert); - if (error != TdsEnums.SNI_SUCCESS) - { - _physicalStateObj.AddError(ProcessSNIError(_physicalStateObj)); - ThrowExceptionAndWarning(_physicalStateObj); - } - - bool valid = serverCallback(serverCert); - if (!valid) - { - throw SQL.InvalidServerCertificate(); - } - } - - // create a new packet encryption changes the internal packet size Bug# 228403 - try - { } // EmptyTry/Finally to avoid FXCop violation - finally - { - _physicalStateObj.ClearAllWritePackets(); - } + EnableSsl(info, encrypt == SqlConnectionEncryptOption.Mandatory, integratedSecurity, serverCallback, clientCallback); } break; @@ -2573,17 +2630,17 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead // ENVCHANGE must be processed synchronously (since it can modify the state of many objects) stateObj._syncOverAsync = true; - SqlEnvChange[] env; + SqlEnvChange env; if (!TryProcessEnvChange(tokenLength, stateObj, out env)) { return false; } - for (int ii = 0; ii < env.Length; ii++) + while (env != null) { - if (env[ii] != null && !this.Connection.IgnoreEnvChange) + if (!Connection.IgnoreEnvChange) { - switch (env[ii].type) + switch (env._type) { case TdsEnums.ENV_BEGINTRAN: case TdsEnums.ENV_ENLISTDTC: @@ -2598,12 +2655,12 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead if (null != _currentTransaction) { - _currentTransaction.TransactionId = env[ii].newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. + _currentTransaction.TransactionId = env._newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. } else { - TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env[ii].type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; - _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue); + TransactionType transactionType = (TdsEnums.ENV_BEGINTRAN == env._type) ? TransactionType.LocalFromTSQL : TransactionType.Distributed; + _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env._newLongValue); } if (null != _statistics && !_statisticsIsInTransaction) { @@ -2629,22 +2686,22 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead // Check null for case where Begin and Rollback obtained in the same message. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { - Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!"); + Debug.Assert(_currentTransaction.TransactionId != env._newLongValue, "transaction id's are not equal!"); } #endif - if (TdsEnums.ENV_COMMITTRAN == env[ii].type) + if (TdsEnums.ENV_COMMITTRAN == env._type) { _currentTransaction.Completed(TransactionState.Committed); } - else if (TdsEnums.ENV_ROLLBACKTRAN == env[ii].type) + else if (TdsEnums.ENV_ROLLBACKTRAN == env._type) { // Hold onto transaction id if distributed tran is rolled back. This must // be sent to the server on subsequent executions even though the transaction // is considered to be rolled back. if (_currentTransaction.IsDistributed && _currentTransaction.IsActive) { - _retainedTransactionId = env[ii].oldLongValue; + _retainedTransactionId = env._oldLongValue; } _currentTransaction.Completed(TransactionState.Aborted); } @@ -2658,10 +2715,15 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead _statisticsIsInTransaction = false; break; default: - _connHandler.OnEnvChange(env[ii]); + _connHandler.OnEnvChange(env); break; } } + + SqlEnvChange head = env; + env = env._next; + head.Clear(); + head = null; } break; } @@ -2947,41 +3009,36 @@ internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataRead return true; } - private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, out SqlEnvChange[] sqlEnvChange) + private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, out SqlEnvChange sqlEnvChange) { // There could be multiple environment change messages following this token. byte byteLength; int processedLength = 0; - int nvalues = 0; - SqlEnvChange[] envarray = new SqlEnvChange[3]; // Why is this hardcoded to 3? + SqlEnvChange head = null; + SqlEnvChange tail = null; sqlEnvChange = null; while (tokenLength > processedLength) { - - if (nvalues >= envarray.Length) + var env = new SqlEnvChange(); + if (!stateObj.TryReadByte(out env._type)) { - // This is a rare path. Most of the time we will have 1 or 2 envchange data streams. - SqlEnvChange[] newenvarray = new SqlEnvChange[envarray.Length + 3]; - - for (int ii = 0; ii < envarray.Length; ii++) - newenvarray[ii] = envarray[ii]; - - envarray = newenvarray; + return false; } - SqlEnvChange env = new SqlEnvChange(); - - if (!stateObj.TryReadByte(out env.type)) + if (head is null) { - return false; + head = env; + tail = env; + } + else + { + tail._next = env; + tail = env; } - envarray[nvalues] = env; - nvalues++; - - switch (env.type) + switch (env._type) { case TdsEnums.ENV_DATABASE: case TdsEnums.ENV_LANG: @@ -2999,18 +3056,18 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - if (env.newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) + if (env._newValue == TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_STRING) { _defaultCodePage = TdsEnums.DEFAULT_ENGLISH_CODE_PAGE_VALUE; _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } else { - Debug.Assert(env.newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); + Debug.Assert(env._newValue.Length > TdsEnums.CHARSET_CODE_PAGE_OFFSET, "TdsParser.ProcessEnvChange(): charset value received with length <=10"); - string stringCodePage = env.newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); + string stringCodePage = env._newValue.Substring(TdsEnums.CHARSET_CODE_PAGE_OFFSET); - _defaultCodePage = Int32.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture); + _defaultCodePage = int.Parse(stringCodePage, NumberStyles.Integer, CultureInfo.InvariantCulture); _defaultEncoding = System.Text.Encoding.GetEncoding(_defaultCodePage); } @@ -3027,7 +3084,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, // Only set on physical state object - this should only occur on LoginAck prior // to MARS initialization! - Int32 packetSize = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + int packetSize = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); if (_physicalStateObj.SetPacketSize(packetSize)) { @@ -3036,8 +3093,8 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, _physicalStateObj.ClearAllWritePackets(); // Update SNI ConsumerInfo value to be resulting packet size - UInt32 unsignedPacketSize = (UInt32)packetSize; - UInt32 result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize); + uint unsignedPacketSize = (uint)packetSize; + uint result = SNINativeMethodWrapper.SNISetInfo(_physicalStateObj.Handle, SNINativeMethodWrapper.QTypes.SNI_QUERY_CONN_BUFSIZE, ref unsignedPacketSize); Debug.Assert(result == TdsEnums.SNI_SUCCESS, "Unexpected failure state upon calling SNISetInfo"); } @@ -3051,7 +3108,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - _defaultLCID = Int32.Parse(env.newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); + _defaultLCID = int.Parse(env._newValue, NumberStyles.Integer, CultureInfo.InvariantCulture); break; case TdsEnums.ENV_COMPFLAGS: @@ -3062,37 +3119,32 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, break; case TdsEnums.ENV_COLLATION: - Debug.Assert(env.newLength == 5 || env.newLength == 0, "Improper length in new collation!"); + Debug.Assert(env._newLength == 5 || env._newLength == 0, "Improper length in new collation!"); if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.newLength = byteLength; - if (env.newLength == 5) + env._newLength = byteLength; + if (env._newLength == 5) { - if (!TryProcessCollation(stateObj, out env.newCollation)) + if (!TryProcessCollation(stateObj, out env._newCollation)) { return false; } // give the parser the new collation values in case parameters don't specify one - _defaultCollation = env.newCollation; - _defaultLCID = env.newCollation.LCID; + _defaultCollation = env._newCollation; + _defaultLCID = env._newCollation.LCID; - int newCodePage = GetCodePage(env.newCollation, stateObj); - if (env.newCollation.IsUTF8) - { // UTF8 collation + // UTF8 collation + if (env._newCollation.IsUTF8) + { _defaultEncoding = Encoding.UTF8; - - if (newCodePage != _defaultCodePage) - { - _defaultCodePage = newCodePage; - } } else { - + int newCodePage = GetCodePage(env._newCollation, stateObj); if (newCodePage != _defaultCodePage) { _defaultCodePage = newCodePage; @@ -3105,17 +3157,17 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 5 || env.oldLength == 0, "Improper length in old collation!"); - if (env.oldLength == 5) + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 5 || env._oldLength == 0, "Improper length in old collation!"); + if (env._oldLength == 5) { - if (!TryProcessCollation(stateObj, out env.oldCollation)) + if (!TryProcessCollation(stateObj, out env._oldCollation)) { return false; } } - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_BEGINTRAN: @@ -3130,44 +3182,44 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = byteLength; - Debug.Assert(env.newLength == 0 || env.newLength == 8, "Improper length for new transaction id!"); + env._newLength = byteLength; + Debug.Assert(env._newLength == 0 || env._newLength == 8, "Improper length for new transaction id!"); - if (env.newLength > 0) + if (env._newLength > 0) { - if (!stateObj.TryReadInt64(out env.newLongValue)) + if (!stateObj.TryReadInt64(out env._newLongValue)) { return false; } - Debug.Assert(env.newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._newLongValue != SqlInternalTransaction.NullTransactionId, "New transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._newLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } if (!stateObj.TryReadByte(out byteLength)) { return false; } - env.oldLength = byteLength; - Debug.Assert(env.oldLength == 0 || env.oldLength == 8, "Improper length for old transaction id!"); + env._oldLength = byteLength; + Debug.Assert(env._oldLength == 0 || env._oldLength == 8, "Improper length for old transaction id!"); - if (env.oldLength > 0) + if (env._oldLength > 0) { - if (!stateObj.TryReadInt64(out env.oldLongValue)) + if (!stateObj.TryReadInt64(out env._oldLongValue)) { return false; } - Debug.Assert(env.oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. + Debug.Assert(env._oldLongValue != SqlInternalTransaction.NullTransactionId, "Old transaction id is null?"); // the server guarantees that zero is an invalid transaction id. } else { - env.oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. + env._oldLongValue = SqlInternalTransaction.NullTransactionId; // the server guarantees that zero is an invalid transaction id. } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; break; case TdsEnums.ENV_LOGSHIPNODE: @@ -3182,12 +3234,12 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, case TdsEnums.ENV_PROMOTETRANSACTION: Debug.Assert(_is2005, "Received new ENVCHANGE tokens on pre 9.0 server!"); - if (!stateObj.TryReadInt32(out env.newLength)) + if (!stateObj.TryReadInt32(out env._newLength)) { // new value has 4 byte length return false; } - env.newBinValue = new byte[env.newLength]; - if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) + env._newBinValue = new byte[env._newLength]; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { // read new value with 4 byte length return false; } @@ -3196,11 +3248,11 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.oldLength = byteLength; - Debug.Assert(0 == env.oldLength, "old length should be zero"); + env._oldLength = byteLength; + Debug.Assert(0 == env._oldLength, "old length should be zero"); // env.length includes 1 byte for type token - env.length = 5 + env.newLength; + env._length = 5 + env._newLength; break; case TdsEnums.ENV_TRANSACTIONMANAGERADDRESS: @@ -3227,7 +3279,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newLength = newLength; + env._newLength = newLength; byte protocol; if (!stateObj.TryReadByte(out protocol)) { @@ -3248,7 +3300,7 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.newRoutingInfo = new RoutingInfo(protocol, port, serverName); + env._newRoutingInfo = new RoutingInfo(protocol, port, serverName); UInt16 oldLength; if (!stateObj.TryReadUInt16(out oldLength)) { @@ -3258,17 +3310,17 @@ private bool TryProcessEnvChange(int tokenLength, TdsParserStateObject stateObj, { return false; } - env.length = env.newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] + env._length = env._newLength + oldLength + 5; // 5=2*sizeof(UInt16)+sizeof(byte) [token+newLength+oldLength] break; default: - Debug.Fail("Unknown environment change token: " + env.type); + Debug.Fail("Unknown environment change token: " + env._type); break; } - processedLength += env.length; + processedLength += env._length; } - sqlEnvChange = envarray; + sqlEnvChange = head; return true; } @@ -3280,9 +3332,10 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.newLength = byteLength; - env.newBinValue = new byte[env.newLength]; - if (!stateObj.TryReadByteArray(env.newBinValue, 0, env.newLength)) + env._newLength = byteLength; + env._newBinValue = ArrayPool.Shared.Rent(env._newLength); + env._newBinRented = true; + if (!stateObj.TryReadByteArray(env._newBinValue, env._newLength)) { return false; } @@ -3290,15 +3343,16 @@ private bool TryReadTwoBinaryFields(SqlEnvChange env, TdsParserStateObject state { return false; } - env.oldLength = byteLength; - env.oldBinValue = new byte[env.oldLength]; - if (!stateObj.TryReadByteArray(env.oldBinValue, 0, env.oldLength)) + env._oldLength = byteLength; + env._oldBinValue = ArrayPool.Shared.Rent(env._oldLength); + env._oldBinRented = true; + if (!stateObj.TryReadByteArray(env._oldBinValue, env._oldLength)) { return false; } // env.length includes 1 byte type token - env.length = 3 + env.newLength + env.oldLength; + env._length = 3 + env._newLength + env._oldLength; return true; } @@ -3324,13 +3378,13 @@ private bool TryReadTwoStringFields(SqlEnvChange env, TdsParserStateObject state return false; } - env.newLength = newLength; - env.newValue = newValue; - env.oldLength = oldLength; - env.oldValue = oldValue; + env._newLength = newLength; + env._newValue = newValue; + env._oldLength = oldLength; + env._oldValue = oldValue; // env.length includes 1 byte type token - env.length = 3 + env.newLength * 2 + env.oldLength * 2; + env._length = 3 + env._newLength * 2 + env._oldLength * 2; return true; } @@ -3592,7 +3646,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) byte[] data = new byte[dataLen]; if (dataLen > 0) { - if (!stateObj.TryReadByteArray(data, 0, checked((int)dataLen))) + if (!stateObj.TryReadByteArray(data, checked((int)dataLen))) { return false; } @@ -3605,7 +3659,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) bool ret = false; if (_connHandler._cleanSQLDNSCaching) { - ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCahce); + ret = SQLFallbackDNSCache.Instance.DeleteDNSInfo(FQDNforDNSCache); } if (_connHandler.IsSQLDNSCachingSupported && _connHandler.pendingSQLDNSObject != null @@ -3631,7 +3685,7 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) if (TceVersionSupported < TdsEnums.MIN_TCE_VERSION_WITH_ENCLAVE_SUPPORT) { // Check if enclave attestation url was specified and server does not support enclave computations and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) && attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.EnclaveComputationsNotSupported(); } @@ -3639,13 +3693,13 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) { throw SQL.AttestationURLNotSupported(); } - else if (SqlConnectionAttestationProtocol.NotSpecified != _connHandler.ConnectionOptions.AttestationProtocol) + else if (_connHandler.ConnectionOptions.AttestationProtocol != SqlConnectionAttestationProtocol.NotSpecified) { throw SQL.AttestationProtocolNotSupported(); } } // Check if enclave attestation url was specified and server does not return an enclave type and we aren't going to be routed to another server. - if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl)) + if (!string.IsNullOrWhiteSpace(_connHandler.ConnectionOptions.EnclaveAttestationUrl) || attestationProtocol == SqlConnectionAttestationProtocol.None) { if (string.IsNullOrWhiteSpace(EnclaveType)) { @@ -3654,9 +3708,9 @@ private bool TryProcessFeatureExtAck(TdsParserStateObject stateObj) else { // Check if the attestation protocol is specified and supports the enclave type. - if (SqlConnectionAttestationProtocol.NotSpecified != attestationProtocol && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) + if (attestationProtocol != SqlConnectionAttestationProtocol.NotSpecified && !IsValidAttestationProtocol(attestationProtocol, EnclaveType)) { - throw SQL.AttestationProtocolNotSupportEnclaveType(ConvertAttestationProtocolToString(attestationProtocol), EnclaveType); + throw SQL.AttestationProtocolNotSupportEnclaveType(attestationProtocol.ToString(), EnclaveType); } } } @@ -3671,10 +3725,8 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta { case TdsEnums.ENCLAVE_TYPE_VBS: if (attestationProtocol != SqlConnectionAttestationProtocol.AAS -#if ENCLAVE_SIMULATOR - && attestationProtocol != SqlConnectionAttestationProtocol.SIM -#endif - && attestationProtocol != SqlConnectionAttestationProtocol.HGS) + && attestationProtocol != SqlConnectionAttestationProtocol.HGS + && attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3683,7 +3735,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta case TdsEnums.ENCLAVE_TYPE_SGX: #if ENCLAVE_SIMULATOR if (attestationProtocol != SqlConnectionAttestationProtocol.AAS - && attestationProtocol != SqlConnectionAttestationProtocol.SIM) + && attestationProtocol != SqlConnectionAttestationProtocol.None) #else if (attestationProtocol != SqlConnectionAttestationProtocol.AAS) #endif @@ -3694,7 +3746,7 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta #if ENCLAVE_SIMULATOR case TdsEnums.ENCLAVE_TYPE_SIMULATOR: - if (attestationProtocol != SqlConnectionAttestationProtocol.SIM) + if (attestationProtocol != SqlConnectionAttestationProtocol.None) { return false; } @@ -3708,26 +3760,6 @@ private bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol attesta return true; } - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } - private bool TryReadByteString(TdsParserStateObject stateObj, out string value) { value = string.Empty; @@ -4002,7 +4034,7 @@ private bool TryProcessSessionState(TdsParserStateObject stateObj, int length, S } if (buffer != null) { - if (!stateObj.TryReadByteArray(buffer, 0, stateLen)) + if (!stateObj.TryReadByteArray(buffer, stateLen)) { return false; } @@ -4039,14 +4071,14 @@ private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck s return false; } - byte[] b = new byte[TdsEnums.VERSION_SIZE]; - if (!stateObj.TryReadByteArray(b, 0, b.Length)) + Span b = stackalloc byte[TdsEnums.VERSION_SIZE]; + if (!stateObj.TryReadByteArray(b, b.Length)) { return false; } - a.tdsVersion = (UInt32)((((((b[0] << 8) | b[1]) << 8) | b[2]) << 8) | b[3]); // bytes are in motorola order (high byte first) - UInt32 majorMinor = a.tdsVersion & 0xff00ffff; - UInt32 increment = (a.tdsVersion >> 16) & 0xff; + a.tdsVersion = (uint)((((((b[0] << 8) | b[1]) << 8) | b[2]) << 8) | b[3]); // bytes are in motorola order (high byte first) + uint majorMinor = a.tdsVersion & 0xff00ffff; + uint increment = (a.tdsVersion >> 16) & 0xff; // Server responds: // 0x07000000 -> 7.0 // Notice server response format is different for bwd compat @@ -4090,10 +4122,16 @@ private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck s { throw SQL.InvalidTDSVersion(); } _is2012 = true; break; + case (uint)TdsEnums.TDS8_MAJOR << 24 | TdsEnums.TDS8_MINOR: + if (increment != TdsEnums.TDS8_INCREMENT) + { throw SQL.InvalidTDSVersion(); } + _is2022 = true; + break; default: throw SQL.InvalidTDSVersion(); } + _is2012 |= _is2022; _is2008 |= _is2012; _is2005 |= _is2008; _is2000SP1 |= _is2005; // includes all lower versions @@ -4189,7 +4227,7 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, // read the rest of the token byte[] tokenData = new byte[tokenLen]; int totalRead = 0; - bool successfulRead = stateObj.TryReadByteArray(tokenData, 0, tokenLen, out totalRead); + bool successfulRead = stateObj.TryReadByteArray(tokenData, tokenLen, out totalRead); if (SqlClientEventSource.Log.IsAdvancedTraceOn()) { SqlClientEventSource.Log.AdvancedTraceEvent(" Read rest of FEDAUTHINFO token stream: {0}", BitConverter.ToString(tokenData, 0, totalRead)); @@ -5189,7 +5227,7 @@ internal bool TryReadCipherInfoEntry(TdsParserStateObject stateObj, out SqlTceCi // Read the key MD Version byte[] keyMDVersion = new byte[8]; - if (!stateObj.TryReadByteArray(keyMDVersion, 0, 8)) + if (!stateObj.TryReadByteArray(keyMDVersion, 8)) { return false; } @@ -5222,7 +5260,7 @@ internal bool TryReadCipherInfoEntry(TdsParserStateObject stateObj, out SqlTceCi encryptedCek = new byte[length]; // Read the actual encrypted CEK - if (!stateObj.TryReadByteArray(encryptedCek, 0, length)) + if (!stateObj.TryReadByteArray(encryptedCek, length)) { return false; } @@ -6679,19 +6717,19 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt case TdsEnums.SQLTIME: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 5, "invalid length for time type!"); - value.SetToTime(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToTime(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; case TdsEnums.SQLDATETIME2: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 8, "invalid length for datetime2 type!"); - value.SetToDateTime2(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToDateTime2(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; case TdsEnums.SQLDATETIMEOFFSET: // We normalize to maximum precision to allow conversion across different precisions. Debug.Assert(length == 10, "invalid length for datetimeoffset type!"); - value.SetToDateTimeOffset(unencryptedBytes, length, TdsEnums.MAX_TIME_SCALE, denormalizedScale); + value.SetToDateTimeOffset(unencryptedBytes, TdsEnums.MAX_TIME_SCALE, denormalizedScale); break; default: @@ -6714,7 +6752,7 @@ internal bool TryReadSqlValue(SqlBuffer value, int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, - string columnName, + string columnName, SqlCommand command = null) { bool isPlp = md.metaType.IsPlp; @@ -6752,8 +6790,7 @@ internal bool TryReadSqlValue(SqlBuffer value, { // If we are given -1 for length, then we read the entire value, // otherwise only the requested amount, usually first chunk. - int ignored; - if (!stateObj.TryReadPlpBytes(ref b, 0, length, out ignored)) + if (!stateObj.TryReadPlpBytes(ref b, 0, length, out _)) { return false; } @@ -6762,7 +6799,7 @@ internal bool TryReadSqlValue(SqlBuffer value, { //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column"); b = new byte[length]; - if (!stateObj.TryReadByteArray(b, 0, length)) + if (!stateObj.TryReadByteArray(b, length)) { return false; } @@ -6846,33 +6883,34 @@ internal bool TryReadSqlValue(SqlBuffer value, private bool TryReadSqlDateTime(SqlBuffer value, byte tdsType, int length, byte scale, TdsParserStateObject stateObj) { - byte[] datetimeBuffer = new byte[length]; + Span datetimeBuffer = ((uint)length <= 16) ? stackalloc byte[16] : new byte[length]; - if (!stateObj.TryReadByteArray(datetimeBuffer, 0, length)) + if (!stateObj.TryReadByteArray(datetimeBuffer, length)) { return false; } + ReadOnlySpan dateTimeData = datetimeBuffer.Slice(0, length); switch (tdsType) { case TdsEnums.SQLDATE: Debug.Assert(length == 3, "invalid length for date type!"); - value.SetToDate(datetimeBuffer); + value.SetToDate(dateTimeData); break; case TdsEnums.SQLTIME: Debug.Assert(3 <= length && length <= 5, "invalid length for time type!"); - value.SetToTime(datetimeBuffer, length, scale, scale); + value.SetToTime(dateTimeData, scale, scale); break; case TdsEnums.SQLDATETIME2: Debug.Assert(6 <= length && length <= 8, "invalid length for datetime2 type!"); - value.SetToDateTime2(datetimeBuffer, length, scale, scale); + value.SetToDateTime2(dateTimeData, scale, scale); break; case TdsEnums.SQLDATETIMEOFFSET: Debug.Assert(8 <= length && length <= 10, "invalid length for datetimeoffset type!"); - value.SetToDateTimeOffset(datetimeBuffer, length, scale, scale); + value.SetToDateTimeOffset(dateTimeData, scale, scale); break; default: @@ -7069,7 +7107,7 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, { b = new byte[GUID_SIZE]; } - if (!stateObj.TryReadByteArray(b, 0, length)) + if (!stateObj.TryReadByteArray(b, length)) { return false; } @@ -7087,7 +7125,7 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, // Note: Better not come here with plp data!! Debug.Assert(length <= TdsEnums.MAXSIZE); byte[] b = new byte[length]; - if (!stateObj.TryReadByteArray(b, 0, length)) + if (!stateObj.TryReadByteArray(b, length)) { return false; } @@ -8751,7 +8789,8 @@ internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData, - SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo) + SqlClientOriginalNetworkAddressInfo originalNetworkAddressInfo, + SqlConnectionEncryptOption encrypt) { _physicalStateObj.SetTimeoutSeconds(rec.timeout); @@ -8949,7 +8988,14 @@ internal void TdsLogin(SqlLogin rec, WriteInt(length, _physicalStateObj); if (recoverySessionData == null) { - WriteInt((TdsEnums.SQL2012_MAJOR << 24) | (TdsEnums.SQL2012_INCREMENT << 16) | TdsEnums.SQL2012_MINOR, _physicalStateObj); + if (encrypt == SqlConnectionEncryptOption.Strict) + { + WriteInt((TdsEnums.TDS8_MAJOR << 24) | (TdsEnums.TDS8_INCREMENT << 16) | TdsEnums.TDS8_MINOR, _physicalStateObj); + } + else + { + WriteInt((TdsEnums.SQL2012_MAJOR << 24) | (TdsEnums.SQL2012_INCREMENT << 16) | TdsEnums.SQL2012_MINOR, _physicalStateObj); + } } else { @@ -9318,7 +9364,7 @@ private void ProcessSSPI(int receivedLength) // read SSPI data received from server Debug.Assert(_physicalStateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - bool result = _physicalStateObj.TryReadByteArray(receivedBuff, 0, receivedLength); + bool result = _physicalStateObj.TryReadByteArray(receivedBuff, receivedLength); if (!result) { throw SQL.SynchronousCallMayNotPend(); } @@ -10454,7 +10500,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo if (releaseConnectionLock) { task.ContinueWith( - static (Task _, object state) => ((TdsParser)state)._connHandler._parserLock.Release(), + static (Task _, object state) => ((TdsParser)state)._connHandler._parserLock.Release(), state: this, scheduler: TaskScheduler.Default ); @@ -10695,7 +10741,7 @@ private void WriteParameterName(string parameterName, TdsParserStateObject state } } - private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly(); + private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly(); private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool isAnonymous, bool advancedTraceIsOn) { // @@ -10770,7 +10816,6 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa value, typeCode, param.Offset, - 0 < param.Size ? param.Size : -1, peekAhead); } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index c708ebc6a3..12fc3ffd1b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -312,23 +312,6 @@ internal RoutingInfo(byte protocol, UInt16 port, string servername) } } - sealed internal class SqlEnvChange - { - internal byte type; - internal byte oldLength; - internal int newLength; // 7206 TDS changes makes this length an int - internal int length; - internal string newValue; - internal string oldValue; - internal byte[] newBinValue; - internal byte[] oldBinValue; - internal long newLongValue; - internal long oldLongValue; - internal SqlCollation newCollation; - internal SqlCollation oldCollation; - internal RoutingInfo newRoutingInfo; - } - sealed internal class SqlLogin { internal SqlAuthenticationMethod authentication = SqlAuthenticationMethod.NotSpecified; // Authentication type diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs index 591bd4f0a7..c2472d0c66 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.cs @@ -150,7 +150,9 @@ internal SNIHandle( TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, SqlConnectionIPAddressPreference ipPreference, - SQLDNSInfo cachedDNSInfo) + SQLDNSInfo cachedDNSInfo, + bool tlsFirst, + string hostNameInCertificate) : base(IntPtr.Zero, true) { @@ -172,7 +174,7 @@ internal SNIHandle( int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState; _status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle, spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, transparentNetworkResolutionStateNo, totalTimeout, - ADP.IsAzureSqlServerEndpoint(serverName), ipPreference, cachedDNSInfo); + ADP.IsAzureSqlServerEndpoint(serverName), ipPreference, cachedDNSInfo, hostNameInCertificate); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index b22faacb58..6e8afce1ea 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -88,7 +88,6 @@ internal int ObjectID internal int _inBytesUsed = 0; // number of bytes used in internal read buffer internal int _inBytesRead = 0; // number of bytes read into internal read buffer internal int _inBytesPacket = 0; // number of bytes left in packet - internal int _spid; // SPID of the current connection // Packet state variables @@ -322,9 +321,8 @@ internal TdsParserStateObject(TdsParser parser, SNIHandle physicalConnection, bo SetPacketSize(_parser._physicalStateObj._outBuff.Length); SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); - SQLDNSInfo cachedDNSInfo; - bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCahce, out cachedDNSInfo); + bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(_parser.FQDNforDNSCache, out cachedDNSInfo); _sessionHandle = new SNIHandle(myInfo, physicalConnection, _parser.Connection.ConnectionOptions.IPAddressPreference, cachedDNSInfo); if (_sessionHandle.Status != TdsEnums.SNI_SUCCESS) @@ -580,7 +578,7 @@ internal bool TryInitialize(TdsParserStateObject stateObj, int columnsCount) } // read the null bitmap compression information from TDS - if (!stateObj.TryReadByteArray(_nullBitmap, 0, _nullBitmap.Length)) + if (!stateObj.TryReadByteArray(_nullBitmap, _nullBitmap.Length)) { return false; } @@ -846,8 +844,21 @@ private SNINativeMethodWrapper.ConsumerInfo CreateConsumerInfo(bool async) return myInfo; } - internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeout, long timerExpire, out byte[] instanceName, byte[] spnBuffer, bool flushCache, - bool async, bool fParallel, TransparentNetworkResolutionState transparentNetworkResolutionState, int totalTimeout, SqlConnectionIPAddressPreference ipPreference, string cachedFQDN) + internal void CreatePhysicalSNIHandle( + string serverName, + bool ignoreSniOpenTimeout, + long timerExpire, + out byte[] instanceName, + byte[] spnBuffer, + bool flushCache, + bool async, + bool fParallel, + TransparentNetworkResolutionState transparentNetworkResolutionState, + int totalTimeout, + SqlConnectionIPAddressPreference ipPreference, + string cachedFQDN, + bool tlsFirst = false, + string hostNameInCertificate = "") { SNINativeMethodWrapper.ConsumerInfo myInfo = CreateConsumerInfo(async); @@ -872,11 +883,12 @@ internal void CreatePhysicalSNIHandle(string serverName, bool ignoreSniOpenTimeo // serverName : serverInfo.ExtendedServerName // may not use this serverName as key - SQLDNSInfo cachedDNSInfo; - bool ret = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out cachedDNSInfo); - _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), - out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, ipPreference, cachedDNSInfo); + _ = SQLFallbackDNSCache.Instance.GetDNSInfo(cachedFQDN, out SQLDNSInfo cachedDNSInfo); + + _sessionHandle = new SNIHandle(myInfo, serverName, spnBuffer, ignoreSniOpenTimeout, checked((int)timeout), + out instanceName, flushCache, !async, fParallel, transparentNetworkResolutionState, totalTimeout, + ipPreference, cachedDNSInfo, tlsFirst, hostNameInCertificate); } internal bool Deactivate() @@ -1389,15 +1401,14 @@ internal bool TryPeekByte(out byte value) // Takes a byte array, an offset, and a len and fills the array from the offset to len number of // bytes from the in buffer. - public bool TryReadByteArray(byte[] buff, int offset, int len) + public bool TryReadByteArray(Span buff, int len) { - int ignored; - return TryReadByteArray(buff, offset, len, out ignored); + return TryReadByteArray(buff, len, out _); } // NOTE: This method must be retriable WITHOUT replaying a snapshot // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead) + public bool TryReadByteArray(Span buff, int len, out int totalRead) { TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method totalRead = 0; @@ -1421,7 +1432,7 @@ public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead } #endif - Debug.Assert(buff == null || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); + Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); // loop through and read up to array length while (len > 0) @@ -1436,9 +1447,11 @@ public bool TryReadByteArray(byte[] buff, int offset, int len, out int totalRead int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (buff != null) + if (!buff.IsEmpty) { - Buffer.BlockCopy(_inBuff, _inBytesUsed, buff, offset + totalRead, bytesToRead); + ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); + Span copyTo = buff.Slice(totalRead, bytesToRead); + copyFrom.CopyTo(copyTo); } totalRead += bytesToRead; @@ -1510,7 +1523,7 @@ internal bool TryReadChar(out char value) // If the char isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = '\0'; return false; @@ -1547,7 +1560,7 @@ internal bool TryReadInt16(out short value) // If the int16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = default(short); return false; @@ -1582,7 +1595,7 @@ internal bool TryReadInt32(out int value) // If the int isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 4)) + if (!TryReadByteArray(_bTmp, 4)) { value = 0; return false; @@ -1626,7 +1639,7 @@ internal bool TryReadInt64(out long value) // then use ReadByteArray since the logic is there to take care of that. int bytesRead = 0; - if (!TryReadByteArray(_bTmp, _bTmpRead, 8 - _bTmpRead, out bytesRead)) + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 8 - _bTmpRead, out bytesRead)) { Debug.Assert(_bTmpRead + bytesRead <= 8, "Read more data than required"); _bTmpRead += bytesRead; @@ -1668,7 +1681,7 @@ internal bool TryReadUInt16(out ushort value) // If the uint16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 2)) + if (!TryReadByteArray(_bTmp, 2)) { value = default(ushort); return false; @@ -1713,7 +1726,7 @@ internal bool TryReadUInt32(out uint value) // then use ReadByteArray since the logic is there to take care of that. int bytesRead = 0; - if (!TryReadByteArray(_bTmp, _bTmpRead, 4 - _bTmpRead, out bytesRead)) + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 4 - _bTmpRead, out bytesRead)) { Debug.Assert(_bTmpRead + bytesRead <= 4, "Read more data than required"); _bTmpRead += bytesRead; @@ -1753,7 +1766,7 @@ internal bool TryReadSingle(out float value) // If the float isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 4)) + if (!TryReadByteArray(_bTmp, 4)) { value = default(float); return false; @@ -1787,7 +1800,7 @@ internal bool TryReadDouble(out double value) // If the double isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(_bTmp, 0, 8)) + if (!TryReadByteArray(_bTmp, 8)) { value = default(double); return false; @@ -1827,7 +1840,7 @@ internal bool TryReadString(int length, out string value) _bTmp = new byte[cBytes]; } - if (!TryReadByteArray(_bTmp, 0, cBytes)) + if (!TryReadByteArray(_bTmp, cBytes)) { value = null; return false; @@ -1905,7 +1918,7 @@ internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encodin _bTmp = new byte[length]; } - if (!TryReadByteArray(_bTmp, 0, length)) + if (!TryReadByteArray(_bTmp, length)) { value = null; return false; @@ -2014,10 +2027,12 @@ internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) int value; int bytesToRead = (int)Math.Min(_longlenleft, (ulong)len); - bool result = TryReadByteArray(buff, offset, bytesToRead, out value); + bool result = TryReadByteArray(buff.AsSpan(start: offset), bytesToRead, out value); _longlenleft -= (ulong)bytesToRead; if (!result) - { throw SQL.SynchronousCallMayNotPend(); } + { + throw SQL.SynchronousCallMayNotPend(); + } return value; } @@ -2091,7 +2106,7 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offst, int len, out int total buff = newbuf; } - bool result = TryReadByteArray(buff, offst, bytesToRead, out bytesRead); + bool result = TryReadByteArray(buff.AsSpan(start: offst), bytesToRead, out bytesRead); Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); @@ -2138,7 +2153,7 @@ internal bool TrySkipLongBytes(long num) while (num > 0) { cbSkip = (int)Math.Min((long)Int32.MaxValue, num); - if (!TryReadByteArray(null, 0, cbSkip)) + if (!TryReadByteArray(Span.Empty, cbSkip)) { return false; } @@ -2152,7 +2167,7 @@ internal bool TrySkipLongBytes(long num) internal bool TrySkipBytes(int num) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - return TryReadByteArray(null, 0, num); + return TryReadByteArray(Span.Empty, num); } ///////////////////////////////////////// @@ -2622,7 +2637,7 @@ internal void ReadSni(TaskCompletionSource completion) ChangeNetworkPacketTimeout(Timeout.Infinite, Timeout.Infinite); } else if (msecsRemaining == 0) - { + { // Got IO Pending, but we have no time left to wait // disable the timer and set the error state by calling OnTimeoutSync ChangeNetworkPacketTimeout(Timeout.Infinite, Timeout.Infinite); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs deleted file mode 100644 index df63fef962..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/sqlinternaltransaction.cs +++ /dev/null @@ -1,588 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data; -using System.Diagnostics; -using System.Threading; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal enum TransactionState - { - Pending = 0, - Active = 1, - Aborted = 2, - Committed = 3, - Unknown = 4, - } - - internal enum TransactionType - { - LocalFromTSQL = 1, - LocalFromAPI = 2, - Delegated = 3, - Distributed = 4, - Context = 5, // only valid in proc. - } - - sealed internal class SqlInternalTransaction - { - internal const long NullTransactionId = 0; - - private TransactionState _transactionState; - private TransactionType _transactionType; - private long _transactionId; // passed in the MARS headers - private int _openResultCount; // passed in the MARS headers - private SqlInternalConnection _innerConnection; - private bool _disposing; // used to prevent us from throwing exceptions while we're disposing - private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. - - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - - internal bool RestoreBrokenConnection { get; set; } - internal bool ConnectionHasBeenRestored { get; set; } - - internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction) : this(innerConnection, type, outerTransaction, NullTransactionId) - { - } - - internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) - { - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Created for connection {1}, outer transaction {2}, Type {3}", ObjectID, innerConnection.ObjectID, (null != outerTransaction) ? outerTransaction.ObjectID : -1, (int)type); - - _innerConnection = innerConnection; - _transactionType = type; - - if (null != outerTransaction) - { - _parent = new WeakReference(outerTransaction); - } - - _transactionId = transactionId; - RestoreBrokenConnection = false; - ConnectionHasBeenRestored = false; - } - - internal bool HasParentTransaction - { - get - { - // Return true if we are an API started local transaction, or if we were a TSQL - // started local transaction and were then wrapped with a parent transaction as - // a result of a later API begin transaction. - bool result = ((TransactionType.LocalFromAPI == _transactionType) || - (TransactionType.LocalFromTSQL == _transactionType && _parent != null)); - return result; - } - } - - internal bool IsAborted - { - get - { - return (TransactionState.Aborted == _transactionState); - } - } - - internal bool IsActive - { - get - { - return (TransactionState.Active == _transactionState); - } - } - - internal bool IsCommitted - { - get - { - return (TransactionState.Committed == _transactionState); - } - } - - internal bool IsCompleted - { - get - { - return (TransactionState.Aborted == _transactionState - || TransactionState.Committed == _transactionState - || TransactionState.Unknown == _transactionState); - } - } - - internal bool IsContext - { - get - { - bool result = (TransactionType.Context == _transactionType); - return result; - } - } - - internal bool IsDelegated - { - get - { - bool result = (TransactionType.Delegated == _transactionType); - return result; - } - } - - internal bool IsDistributed - { - get - { - bool result = (TransactionType.Distributed == _transactionType); - return result; - } - } - - internal bool IsLocal - { - get - { - bool result = (TransactionType.LocalFromTSQL == _transactionType - || TransactionType.LocalFromAPI == _transactionType - || TransactionType.Context == _transactionType); - return result; - } - } - - internal bool IsOrphaned - { - get - { - // An internal transaction is orphaned when its parent has been - // reclaimed by GC. - bool result; - if (null == _parent) - { - // No parent, so we better be LocalFromTSQL. Should we even return in this case - - // since it could be argued this is invalid? - Debug.Fail("Why are we calling IsOrphaned with no parent?"); - Debug.Assert(_transactionType == TransactionType.LocalFromTSQL, "invalid state"); - result = false; - } - else if (!_parent.TryGetTarget(out SqlTransaction _)) - { - // We had a parent, but parent was GC'ed. - result = true; - } - else - { - // We have a parent, and parent is alive. - result = false; - } - - return result; - } - } - - internal bool IsZombied - { - get - { - return (null == _innerConnection); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal int OpenResultsCount - { - get - { - return _openResultCount; - } - } - - internal SqlTransaction Parent - { - get - { - SqlTransaction result = null; - // Should we protect against this, since this probably is an invalid state? - Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); - if (_parent != null && _parent.TryGetTarget(out SqlTransaction target)) - { - result = target; - } - return result; - } - } - - internal long TransactionId - { - get - { - return _transactionId; - } - set - { - Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); - _transactionId = value; - } - } - - internal void Activate() - { - _transactionState = TransactionState.Active; - } - - private void CheckTransactionLevelAndZombie() - { - try - { - if (!IsZombied && GetServerTransactionLevel() == 0) - { - // If not zombied, not closed, and not in transaction, zombie. - Zombie(); - } - } - catch (Exception e) - { - // UNDONE - should not be catching all exceptions!!! - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - - ADP.TraceExceptionWithoutRethrow(e); - Zombie(); // If exception caught when trying to check level, zombie. - } - } - - internal void CloseFromConnection() - { - SqlInternalConnection innerConnection = _innerConnection; - - Debug.Assert(innerConnection != null, "How can we be here if the connection is null?"); - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Closing", ObjectID); - - bool processFinallyBlock = true; - try - { - innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); - } - catch (Exception e) - { - processFinallyBlock = ADP.IsCatchableExceptionType(e); - throw; - } - finally - { - TdsParser.ReliabilitySection.Assert("unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method - if (processFinallyBlock) - { - // Always ensure we're zombied; 2005 will send an EnvChange that - // will cause the zombie, but only if we actually go to the wire; - // 7.0 and 2000 won't send the env change, so we have to handle - // them ourselves. - Zombie(); - } - } - } - - internal void Commit() - { - using (TryEventScope.Create(" {0}", ObjectID)) - { - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - // If this transaction has been completed, throw exception since it is unusable. - try - { - // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT - // simply commits the transaction from the most recent BEGIN, nested or otherwise. - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); - - // SQL BU DT 291159 - perform full Zombie on pre-2005, but do not actually - // complete internal transaction until informed by server in the case of 2005 - // or later. - if (!IsZombied && !_innerConnection.Is2005OrNewer) - { - // Since nested transactions are no longer allowed, set flag to false. - // This transaction has been completed. - Zombie(); - } - else - { - ZombieParent(); - } - } - catch (Exception e) - { - // UNDONE - should not be catching all exceptions!!! - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - - throw; - } - } - } - - internal void Completed(TransactionState transactionState) - { - Debug.Assert(TransactionState.Active < transactionState, "invalid transaction completion state?"); - _transactionState = transactionState; - Zombie(); - } - - internal Int32 DecrementAndObtainOpenResultCount() - { - Int32 openResultCount = Interlocked.Decrement(ref _openResultCount); - if (openResultCount < 0) - { - throw SQL.OpenResultCountExceeded(); - } - return openResultCount; - } - - internal void Dispose() - { - this.Dispose(true); - System.GC.SuppressFinalize(this); - } - - private /*protected override*/ void Dispose(bool disposing) - { - SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Disposing", ObjectID); - - if (disposing) - { - if (null != _innerConnection) - { - // implicitly rollback if transaction still valid - _disposing = true; - this.Rollback(); - } - } - } - - private int GetServerTransactionLevel() - { - // This function is needed for those times when it is impossible to determine the server's - // transaction level, unless the user's arguments were parsed - which is something we don't want - // to do. An example when it is impossible to determine the level is after a rollback. - - // TODO: we really ought to be able to execute without using the public objects... - - using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) - { - transactionLevelCommand.Transaction = Parent; - - SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); - parameter.Direction = ParameterDirection.Output; - transactionLevelCommand.Parameters.Add(parameter); - - // UNDONE: use a singleton select here - // UNDONE: execute without SqlClientPermission.Demand() - transactionLevelCommand.RunExecuteReader(0, RunBehavior.UntilDone, false /* returnDataStream */, nameof(GetServerTransactionLevel)); - - return (int)parameter.Value; - } - } - - internal Int32 IncrementAndObtainOpenResultCount() - { - Int32 openResultCount = Interlocked.Increment(ref _openResultCount); - - if (openResultCount < 0) - { - throw SQL.OpenResultCountExceeded(); - } - return openResultCount; - } - - internal void InitParent(SqlTransaction transaction) - { - Debug.Assert(_parent == null, "Why do we have a parent on InitParent?"); - _parent = new WeakReference(transaction); - } - - internal void Rollback() - { - using (TryEventScope.Create(" {0}", ObjectID)) - { - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - try - { - // If no arg is given to ROLLBACK it will rollback to the outermost begin - rolling back - // all nested transactions as well as the outermost transaction. - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.IfRollback, null, IsolationLevel.Unspecified, null, false); - - // Since Rollback will rollback to outermost begin, no need to check - // server transaction level. This transaction has been completed. - Zombie(); - } - catch (Exception e) - { - // UNDONE - should not be catching all exceptions!!! - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - - if (!_disposing) - { - throw; - } - } - else - { - throw; - } - } - } - } - - internal void Rollback(string transactionName) - { - using (TryEventScope.Create(" {0}, transactionName='{1}'", ObjectID, transactionName)) - { - if (_innerConnection.IsLockedForBulkCopy) - { - throw SQL.ConnectionLockedForBcpEvent(); - } - - _innerConnection.ValidateConnectionForExecute(null); - - // ROLLBACK takes either a save point name or a transaction name. It will rollback the - // transaction to either the save point with the save point name or begin with the - // transaction name. NOTE: for simplicity it is possible to give all save point names - // the same name, and ROLLBACK will simply rollback to the most recent save point with the - // save point name. - if (ADP.IsEmpty(transactionName)) - { - throw SQL.NullEmptyTransactionName(); - } - - try - { - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); - - if (!IsZombied && !_innerConnection.Is2005OrNewer) - { - // Check if Zombied before making round-trip to server. - // Against 2005 we receive an envchange on the ExecuteTransaction above on the - // parser that calls back into SqlTransaction for the Zombie() call. - CheckTransactionLevelAndZombie(); - } - } - catch (Exception e) - { - // UNDONE - should not be catching all exceptions!!! - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - throw; - } - } - } - - internal void Save(string savePointName) - { - using (TryEventScope.Create(" {0}, savePointName='{1}'", ObjectID, savePointName)) - { - _innerConnection.ValidateConnectionForExecute(null); - - // ROLLBACK takes either a save point name or a transaction name. It will rollback the - // transaction to either the save point with the save point name or begin with the - // transaction name. So, to rollback a nested transaction you must have a save point. - // SAVE TRANSACTION MUST HAVE AN ARGUMENT!!! Save Transaction without an arg throws an - // exception from the server. So, an overload for SaveTransaction without an arg doesn't make - // sense to have. Save Transaction does not affect the transaction level. - if (ADP.IsEmpty(savePointName)) - { - throw SQL.NullEmptyTransactionName(); - } - - try - { - _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Save, savePointName, IsolationLevel.Unspecified, null, false); - } - catch (Exception e) - { - // UNDONE - should not be catching all exceptions!!! - if (ADP.IsCatchableExceptionType(e)) - { - CheckTransactionLevelAndZombie(); - } - - throw; - } - } - } - - internal void Zombie() - { - // Called by several places in the code to ensure that the outer - // transaction object has been zombied and the parser has broken - // it's reference to us. - - // NOTE: we'll be called from the TdsParser when it gets appropriate - // ENVCHANGE events that indicate the transaction has completed, however - // we cannot rely upon those events occuring in the case of pre-2005 - // servers (and when we don't go to the wire because the connection - // is broken) so we can also be called from the Commit/Rollback/Save - // methods to handle that case as well. - - // There are two parts to a full zombie: - // 1) Zombie parent and disconnect outer transaction from internal transaction - // 2) Disconnect internal transaction from connection and parser - // Number 1 needs to be done whenever a SqlTransaction object is completed. Number - // 2 is only done when a transaction is actually completed. Since users can begin - // transactions both in and outside of the API, and since nested begins are not actual - // transactions we need to distinguish between #1 and #2. See SQL BU DT 291159 - // for further details. - - ZombieParent(); - - SqlInternalConnection innerConnection = _innerConnection; - _innerConnection = null; - - if (null != innerConnection) - { - innerConnection.DisconnectTransaction(this); - } - } - - private void ZombieParent() - { - if (_parent != null && _parent.TryGetTarget(out SqlTransaction parent)) - { - parent.Zombie(); - } - _parent = null; - } - - internal string TraceString() - { - return string.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", - ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs index 40103edee5..ab73d61d19 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlFileStream.cs @@ -714,7 +714,7 @@ Int64 allocationSize try { SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0}, desiredAccess=0x{1}, allocationSize={2}, " + - "fileAttributes=0x{3}, shareAccess=0x{4}, dwCreateDisposition=0x{5}, createOptions=0x{ dwCreateOptions}", ObjectID, (int)nDesiredAccess, allocationSize, 0, (int)shareAccess, dwCreateDisposition); + "fileAttributes=0x{3}, shareAccess=0x{4}, dwCreateDisposition=0x{5}, createOptions=0x{6}", ObjectID, (int)nDesiredAccess, allocationSize, 0, (int)shareAccess, dwCreateDisposition, dwCreateOptions); retval = UnsafeNativeMethods.NtCreateFile(out hFile, nDesiredAccess, ref oa, out ioStatusBlock, ref allocationSize, diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs new file mode 100644 index 0000000000..941e3325b6 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs @@ -0,0 +1,343 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data.SqlTypes; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; +using Microsoft.Data.SqlClient; + +namespace Microsoft.Data.SqlTypes +{ + /// + /// This type provides workarounds for the separation between System.Data.Common + /// and Microsoft.Data.SqlClient. The latter wants to access internal members of the former, and + /// this class provides ways to do that. We must review and update this implementation any time the + /// implementation of the corresponding types in System.Data.Common change. + /// + internal static partial class SqlTypeWorkarounds + { + #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation + private static readonly Func s_sqlMoneyfactory = CtorHelper.CreateFactory(); // binds to SqlMoney..ctor(long, int) if it exists + + /// + /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists + /// only to distinguish this constructor from the constructor that takes a long. + /// Used only internally. + /// + internal static SqlMoney SqlMoneyCtor(long value, int ignored) + { + SqlMoney val; + if (s_sqlMoneyfactory is not null) + { + val = s_sqlMoneyfactory(value); + } + else + { + // SqlMoney is a long internally. Dividing by 10,000 gives us the decimal representation + val = new SqlMoney(((decimal)value) / 10000); + } + + return val; + } + + internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money) + { + return SqlMoneyHelper.s_sqlMoneyToLong(ref money); + } + + private static class SqlMoneyHelper + { + internal delegate long SqlMoneyToLongDelegate(ref SqlMoney @this); + internal static readonly SqlMoneyToLongDelegate s_sqlMoneyToLong = GetSqlMoneyToLong(); + + internal static SqlMoneyToLongDelegate GetSqlMoneyToLong() + { + SqlMoneyToLongDelegate del = null; + try + { + del = GetFastSqlMoneyToLong(); + } + catch + { + // If an exception occurs for any reason, swallow & use the fallback code path. + } + + return del ?? FallbackSqlMoneyToLong; + } + + private static SqlMoneyToLongDelegate GetFastSqlMoneyToLong() + { + MethodInfo toSqlInternalRepresentation = typeof(SqlMoney).GetMethod("ToSqlInternalRepresentation", + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding, + null, CallingConventions.Any, new Type[] { }, null); + + if (toSqlInternalRepresentation is not null && toSqlInternalRepresentation.ReturnType == typeof(long)) + { + // On Full Framework, invoking the MethodInfo first before wrapping + // a delegate around it will produce better codegen. We don't need + // to inspect the return value; we just need to call the method. + + _ = toSqlInternalRepresentation.Invoke(new SqlMoney(0), new object[0]); + + // Now create the delegate. This is an open delegate, meaning the + // "this" parameter will be provided as arg0 on each call. + + var del = (SqlMoneyToLongDelegate)toSqlInternalRepresentation.CreateDelegate(typeof(SqlMoneyToLongDelegate), target: null); + + // Now we can cache the delegate and invoke it over and over again. + // Note: the first parameter to the delegate is provided *byref*. + + return del; + } + + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.GetFastSqlMoneyToLong | Info | SqlMoney.ToSqlInternalRepresentation() not found. Less efficient fallback method will be used."); + return null; // missing the expected method - cannot use fast path + } + + // Used in case we can't use a [Serializable]-like mechanism. + private static long FallbackSqlMoneyToLong(ref SqlMoney value) + { + if (value.IsNull) + { + return default; + } + else + { + decimal data = value.ToDecimal(); + return (long)(data * 10000); + } + } + } + #endregion + + #region Work around inability to access SqlDecimal._data1/2/3/4 + internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) + { + SqlDecimalHelper.s_decompose(d, out data1, out data2, out data3, out data4); + } + + private static class SqlDecimalHelper + { + internal delegate void Decomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4); + internal static readonly Decomposer s_decompose = GetDecomposer(); + + private static Decomposer GetDecomposer() + { + Decomposer decomposer = null; + try + { + decomposer = GetFastDecomposer(); + } + catch + { + // If an exception occurs for any reason, swallow & use the fallback code path. + } + + return decomposer ?? FallbackDecomposer; + } + + private static Decomposer GetFastDecomposer() + { + // This takes advantage of the fact that for [Serializable] types, the member fields are implicitly + // part of the type's serialization contract. This includes the fields' names and types. By default, + // [Serializable]-compliant serializers will read all the member fields and shove the data into a + // SerializationInfo dictionary. We mimic this behavior in a manner consistent with the [Serializable] + // pattern, but much more efficiently. + // + // In order to make sure we're staying compliant, we need to gate our checks to fulfill some core + // assumptions. Importantly, the type must be [Serializable] but cannot be ISerializable, as the + // presence of the interface means that the type wants to be responsible for its own serialization, + // and that member fields are not guaranteed to be part of the serialization contract. Additionally, + // we need to check for [OnSerializing] and [OnDeserializing] methods, because we cannot account + // for any logic which might be present within them. + + if (!typeof(SqlDecimal).IsSerializable) + { + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal isn't Serializable. Less efficient fallback method will be used."); + return null; // type is not serializable - cannot use fast path assumptions + } + + if (typeof(ISerializable).IsAssignableFrom(typeof(SqlDecimal))) + { + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal is ISerializable. Less efficient fallback method will be used."); + return null; // type contains custom logic - cannot use fast path assumptions + } + + foreach (MethodInfo method in typeof(SqlDecimal).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) + { + if (method.IsDefined(typeof(OnDeserializingAttribute)) || method.IsDefined(typeof(OnDeserializedAttribute))) + { + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal contains custom serialization logic. Less efficient fallback method will be used."); + return null; // type contains custom logic - cannot use fast path assumptions + } + } + + // GetSerializableMembers filters out [NonSerialized] fields for us automatically. + + FieldInfo fiData1 = null, fiData2 = null, fiData3 = null, fiData4 = null; + foreach (MemberInfo candidate in FormatterServices.GetSerializableMembers(typeof(SqlDecimal))) + { + if (candidate is FieldInfo fi && fi.FieldType == typeof(uint)) + { + if (fi.Name == "m_data1") + { fiData1 = fi; } + else if (fi.Name == "m_data2") + { fiData2 = fi; } + else if (fi.Name == "m_data3") + { fiData3 = fi; } + else if (fi.Name == "m_data4") + { fiData4 = fi; } + } + } + + if (fiData1 is null || fiData2 is null || fiData3 is null || fiData4 is null) + { + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | Expected SqlDecimal fields are missing. Less efficient fallback method will be used."); + return null; // missing one of the expected member fields - cannot use fast path assumptions + } + + Type refToUInt32 = typeof(uint).MakeByRefType(); + DynamicMethod dm = new( + name: "sqldecimal-decomposer", + returnType: typeof(void), + parameterTypes: new[] { typeof(SqlDecimal), refToUInt32, refToUInt32, refToUInt32, refToUInt32 }, + restrictedSkipVisibility: true); // perf: JITs method at delegate creation time + + ILGenerator ilGen = dm.GetILGenerator(); + ilGen.Emit(OpCodes.Ldarg_1); // eval stack := [UInt32&] + ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] + ilGen.Emit(OpCodes.Ldfld, fiData1); // eval stack := [UInt32&] [UInt32] + ilGen.Emit(OpCodes.Stind_I4); // eval stack := + ilGen.Emit(OpCodes.Ldarg_2); // eval stack := [UInt32&] + ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] + ilGen.Emit(OpCodes.Ldfld, fiData2); // eval stack := [UInt32&] [UInt32] + ilGen.Emit(OpCodes.Stind_I4); // eval stack := + ilGen.Emit(OpCodes.Ldarg_3); // eval stack := [UInt32&] + ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] + ilGen.Emit(OpCodes.Ldfld, fiData3); // eval stack := [UInt32&] [UInt32] + ilGen.Emit(OpCodes.Stind_I4); // eval stack := + ilGen.Emit(OpCodes.Ldarg_S, (byte)4); // eval stack := [UInt32&] + ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal] + ilGen.Emit(OpCodes.Ldfld, fiData4); // eval stack := [UInt32&] [UInt32] + ilGen.Emit(OpCodes.Stind_I4); // eval stack := + ilGen.Emit(OpCodes.Ret); + + return (Decomposer)dm.CreateDelegate(typeof(Decomposer), null /* target */); + } + + // Used in case we can't use a [Serializable]-like mechanism. + private static void FallbackDecomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4) + { + if (value.IsNull) + { + data1 = default; + data2 = default; + data3 = default; + data4 = default; + } + else + { + int[] data = value.Data; // allocation + data4 = (uint)data[3]; // write in reverse to avoid multiple bounds checks + data3 = (uint)data[2]; + data2 = (uint)data[1]; + data1 = (uint)data[0]; + } + } + } + #endregion + + #region Work around inability to access SqlBinary.ctor(byte[], bool) + private static readonly Func s_sqlBinaryfactory = CtorHelper.CreateFactory(); // binds to SqlBinary..ctor(byte[], bool) if it exists + + internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) + { + SqlBinary val; + if (s_sqlBinaryfactory is not null) + { + val = s_sqlBinaryfactory(value); + } + else + { + val = new SqlBinary(value); + } + + return val; + } + #endregion + + #region Work around inability to access SqlGuid.ctor(byte[], bool) + private static readonly Func s_sqlGuidfactory = CtorHelper.CreateFactory(); // binds to SqlGuid..ctor(byte[], bool) if it exists + + internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) + { + SqlGuid val; + if (s_sqlGuidfactory is not null) + { + val = s_sqlGuidfactory(value); + } + else + { + val = new SqlGuid(value); + } + + return val; + } + #endregion + + private static class CtorHelper + { + // Returns null if .ctor(TValue, TIgnored) cannot be found. + // Caller should have fallback logic in place in case the API doesn't exist. + internal unsafe static Func CreateFactory() where TInstance : struct + { + try + { + ConstructorInfo fullCtor = typeof(TInstance).GetConstructor( + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding, + null, new[] { typeof(TValue), typeof(TIgnored) }, null); + if (fullCtor is not null) + { + // Need to use fnptr rather than delegate since MulticastDelegate expects to point to a MethodInfo, + // not a ConstructorInfo. The convention for invoking struct ctors is that the caller zeros memory, + // then passes a ref to the zeroed memory as the implicit arg0 "this". We don't need to worry + // about keeping this pointer alive; the fact that we're instantiated over TInstance will do it + // for us. + // + // On Full Framework, creating a delegate to InvocationHelper before invoking it for the first time + // will cause the delegate to point to the pre-JIT stub, which has an expensive preamble. Instead, + // we invoke InvocationHelper manually with a captured no-op fnptr. We'll then replace it with the + // real fnptr before creating a new delegate (pointing to the real codegen, not the stub) and + // returning that new delegate to our caller. + + static void DummyNoOp(ref TInstance @this, TValue value, TIgnored ignored) + { } + + IntPtr fnPtr; + TInstance InvocationHelper(TValue value) + { + TInstance retVal = default; // ensure zero-inited + ((delegate* managed)fnPtr)(ref retVal, value, default); + return retVal; + } + + fnPtr = (IntPtr)(delegate* managed)(&DummyNoOp); + InvocationHelper(default); // no-op to trigger JIT + + fnPtr = fullCtor.MethodHandle.GetFunctionPointer(); // replace before returning to caller + return InvocationHelper; + } + } + catch + { + } + + SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.CtorHelper.CreateFactory | Info | {0}..ctor({1}, {2}) not found. Less efficient fallback method will be used.", typeof(TInstance).Name, typeof(TValue).Name, typeof(TIgnored).Name); + return null; // factory not found or an exception occurred + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml index af9532a1ae..73ddc2f575 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml @@ -197,7 +197,7 @@ 4 4 SQLCommand - SELECT DB_NAME() AS TYPE_CATALOG, sc.name AS TYPE_SCHEMA, tt.name AS TYPE_NAME, c.name AS MEMBER_NAME, ColumnProperty(c.object_id, c.name, 'ordinal') AS ORDINAL_POSITION, convert(nvarchar(4000), object_definition(c.default_object_id)) AS MEMBER_DEFAULT, convert(varchar(3), CASE c.is_nullable WHEN 1 THEN 'YES' ELSE 'NO' END) AS IS_NULLABLE, type_name(c.system_type_id) AS DATA_TYPE, ColumnProperty(c.object_id, c.name, 'charmaxlen') AS CHARACTER_MAXIMUM_LENGTH, ColumnProperty(c.object_id, c.name, 'octetmaxlen') AS CHARACTER_OCTET_LENGTH, convert(tinyint, CASE /* int/decimal/numeric/real/float/money */ WHEN c.system_type_id IN (48, 52, 56, 59, 60, 62, 106, 108, 122, 127) THEN c.precision END) AS NUMERIC_PRECISION, convert(smallint, CASE /* int/money/decimal/numeric */ WHEN c.system_type_id IN (48, 52, 56, 60, 106, 108, 122, 127) THEN 10 WHEN c.system_type_id IN (59, 62) THEN 2 END) AS NUMERIC_PRECISION_RADIX, /* real/float */ convert(int, CASE /* datetime/smalldatetime */ WHEN c.system_type_id IN (58, 61) THEN NULL ELSE odbcscale(c.system_type_id, c.scale) END) AS NUMERIC_SCALE, convert(smallint, CASE /* datetime/smalldatetime */ WHEN c.system_type_id IN (58, 61) THEN 3 END) AS DATETIME_PRECISION, convert(sysname, null) AS CHARACTER_SET_CATALOG, convert(sysname, null) AS CHARACTER_SET_SCHEMA, convert(sysname, CASE WHEN c.system_type_id IN (35, 167, 175) /*char/varchar/text*/ THEN CollationProperty(c.collation_name, 'sqlcharsetname') WHEN c.system_type_id IN (99, 231, 239) /*nchar/nvarchar/ntext*/ THEN N'UNICODE' END) AS CHARACTER_SET_NAME, convert(sysname, null) AS COLLATION_CATALOG FROM sys.schemas sc join sys.objects o on sc.schema_id = o.schema_id JOIN sys.table_types tt on o.object_id = tt.type_table_object_id JOIN sys.columns c ON c.object_id = o.object_id LEFT JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE o.type IN ('TT') AND (DB_NAME() = @Catalog or (@Catalog is null)) and (sc.name = @Owner or (@Owner is null)) and (tt.name = @Type or (@Type is null)) and (c.name = @Member or (@Member is null)) order by sc.name, tt.name, c.name + SELECT DB_NAME() AS TYPE_CATALOG, sc.name AS TYPE_SCHEMA, tt.name AS TYPE_NAME, c.name AS MEMBER_NAME, ColumnProperty(c.object_id, c.name, 'ordinal') AS ORDINAL_POSITION, convert(nvarchar(4000), object_definition(c.default_object_id)) AS MEMBER_DEFAULT, convert(varchar(3), CASE c.is_nullable WHEN 1 THEN 'YES' ELSE 'NO' END) AS IS_NULLABLE, type_name(c.system_type_id) AS DATA_TYPE, ColumnProperty(c.object_id, c.name, 'charmaxlen') AS CHARACTER_MAXIMUM_LENGTH, ColumnProperty(c.object_id, c.name, 'octetmaxlen') AS CHARACTER_OCTET_LENGTH, convert(tinyint, CASE WHEN c.system_type_id IN /* int/decimal/numeric/real/float/money */ (48, 52, 56, 59, 60, 62, 106, 108, 122, 127) THEN c.precision END) AS NUMERIC_PRECISION, convert(smallint, CASE WHEN c.system_type_id IN /* int/money/decimal/numeric */ (48, 52, 56, 60, 106, 108, 122, 127) THEN 10 WHEN c.system_type_id IN /* real/float */ (59, 62) THEN 2 END) AS NUMERIC_PRECISION_RADIX, convert(int, CASE WHEN c.system_type_id IN /* datetime/smalldatetime */ (58, 61) THEN NULL ELSE odbcscale(c.system_type_id, c.scale) END) AS NUMERIC_SCALE, convert(smallint, CASE WHEN c.system_type_id IN /* datetime/smalldatetime */ (58, 61) THEN 3 END) AS DATETIME_PRECISION, convert(sysname, null) AS CHARACTER_SET_CATALOG, convert(sysname, null) AS CHARACTER_SET_SCHEMA, convert(sysname, CASE WHEN c.system_type_id IN /* char/varchar/text */ (35, 167, 175) THEN CollationProperty(c.collation_name, 'sqlcharsetname') WHEN c.system_type_id IN /* nchar/nvarchar/ntext */ (99, 231, 239) THEN N'UNICODE' END) AS CHARACTER_SET_NAME, convert(sysname, null) AS COLLATION_CATALOG FROM sys.table_types tt join sys.objects o on o.object_id = tt.type_table_object_id JOIN sys.schemas sc on sc.schema_id = tt.schema_id JOIN sys.columns c ON c.object_id = o.object_id LEFT JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE o.type IN ('TT') AND (DB_NAME() = @Catalog or (@Catalog is null)) and (sc.name = @Owner or (@Owner is null)) and (tt.name = @Type or (@Type is null)) and (c.name = @Member or (@Member is null)) order by sc.name, tt.name, c.name 09.00.000.0 diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs index bfaf7407be..fdbc830cb6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace System { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -60,6 +60,15 @@ internal Strings() { } } + /// + /// Looks up a localized string similar to Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2}. + /// + internal static string AAD_Token_Retrieving_Timeout { + get { + return ResourceManager.GetString("AAD_Token_Retrieving_Timeout", resourceCulture); + } + } + /// /// Looks up a localized string similar to Data adapter mapping error.. /// @@ -5694,6 +5703,15 @@ internal static string DbConnectionString_FailoverPartner { } } + /// + /// Looks up a localized string similar to The service principal name (SPN) of the failover partner.. + /// + internal static string DbConnectionString_FailoverPartnerSPN { + get { + return ResourceManager.GetString("DbConnectionString_FailoverPartnerSPN", resourceCulture); + } + } + /// /// Looks up a localized string similar to The UDL file to use when connecting to the Data Source.. /// @@ -5847,6 +5865,15 @@ internal static string DbConnectionString_Replication { } } + /// + /// Looks up a localized string similar to The service principal name (SPN) of the server.. + /// + internal static string DbConnectionString_ServerSPN { + get { + return ResourceManager.GetString("DbConnectionString_ServerSPN", resourceCulture); + } + } + /// /// Looks up a localized string similar to Indicates binding behavior of connection to a System.Transactions Transaction when enlisted.. /// @@ -6757,7 +6784,7 @@ internal static string GT_Disabled { } /// - /// Looks up a localized string similar to The currently loaded System.Transactions.dll does not support Global Transactions. Please upgrade to .NET Framework 4.6.1 or later.. + /// Looks up a localized string similar to The currently loaded System.Transactions.dll does not support Global Transactions. Please upgrade to .NET Framework 4.6.2 or later.. /// internal static string GT_UnsupportedSysTxVersion { get { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx index 1cce66ef3b..8496292680 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.de.resx @@ -2872,7 +2872,7 @@ Globale Transaktionen sind für diese Azure SQL-Datenbank-Instanz nicht aktiviert. Wenden Sie sich an den Azure SQL-Datenbank-Support, um Unterstützung zu erhalten. - Die aktuell geladene Datei "System.Transactions.dll" unterstützt keine globalen Transaktionen. Führen Sie ein Upgrade auf .NET Framework 4.6.1 oder höher durch. + Die aktuell geladene System.Transactions.dll unterstützt keine globalen Transaktionen. Bitte aktualisieren Sie auf .NET Framework 4.6.2 oder höher. Batchupdates werden bei der Kontextverbindung nicht unterstützt. @@ -3925,16 +3925,16 @@ Es wurde eine ungültige Registrierungskonfiguration für die Local Database Runtime gefunden. Überprüfen Sie, ob SQL Server Express richtig installiert ist. - Der Registrierungseintrag für den Dateipfad zur Datei "SQLUserInstance.dll" wurde nicht gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. + Der Registrierungseintrag für den Dateipfad zur Datei SQLUserInstance.dll wurde nicht gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. - Der Registrierungswert enthält einen ungültigen Dateipfad für "SQLUserInstance.dll". Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. + Der Registrierungswert enthält einen ungültigen Dateipfad für SQLUserInstance.dll. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. - "SQLUserInstance.dll" kann nicht von dem in der Registrierung angegebenen Ort geladen werden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. + SQLUserInstance.dll kann nicht von dem in der Registrierung angegebenen Ort geladen werden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. - An dem Ort, der in der Registrierung angegeben ist, wurde eine ungültige "SQLUserInstance.dll" gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. + An dem Ort, der in der Registrierung angegeben ist, wurde eine ungültige SQLUserInstance.dll gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. Netzwerkbezogener oder instanzspezifischer Fehler beim Herstellen einer Verbindung mit SQL Server. Der Server wurde nicht gefunden, oder auf ihn kann nicht zugegriffen werden. Überprüfen Sie, ob der Instanzname richtig ist und ob SQL Server Remoteverbindungen zulässt. @@ -3985,7 +3985,7 @@ Local Database Runtime: "SQLUserInstance.dll" kann nicht geladen werden. - An dem Ort, der in der Registrierung angegeben ist, wurde eine ungültige "SQLUserInstance.dll" gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. + An dem Ort, der in der Registrierung angegeben ist, wurde eine ungültige SQLUserInstance.dll gefunden. Überprüfen Sie, ob die Funktion Local Database Runtime von SQL Server Express richtig installiert ist. Fehlermeldung von Local Database Runtime konnte nicht abgerufen werden. @@ -4617,4 +4617,13 @@ Der Parameter "{0}" kann keine Ausgaberichtung oder InputOutput aufweisen, wenn EnableOptimizedParameterBinding für den übergeordneten Befehl aktiviert ist. + + Timeout bei der Verbindung beim Abrufen eines Zugriffstokens mithilfe der Authentifizierungsmethode "{0}". Letzter Fehler: {1}: {2} + + + Der Dienstprinzipalname (SPN) des Failoverpartners. + + + Der Dienstprinzipalname (SPN) des Servers. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx index 2bd80b50ef..a63203c09b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.es.resx @@ -2872,7 +2872,7 @@ Las transacciones globales no están habilitadas para esta base de datos SQL de Azure. Para obtener ayuda, póngase en contacto con el soporte técnico de Azure SQL Database. - El archivo System.Transactions.dll cargado actualmente no es compatible con las transacciones globales. Actualice a .NET Framework 4.6.1 o posterior. + El archivo System.Transactions.dll cargado actualmente no es compatible con las transacciones globales. Actualice a .NET Framework 4.6.2 o posterior. La conexión de contexto no admite la actualización de procesamiento por lotes. @@ -3922,19 +3922,19 @@ No se puede ubicar la instalación de Local Database Runtime. Compruebe que SQL Server Express se ha instalado correctamente y que se ha habilitado la característica de tiempo de ejecución de la base de datos local. - La configuración del Registro de Local Database Runtime no es válida. Compruebe que SQL Server Express se ha instalado correctamente. + La configuración del registro de Local Database Runtime no es válida. Compruebe que SQL Server Express se ha instalado correctamente. - No se puede encontrar la entrada del Registro para la ruta del archivo SQLUserInstance.dll.Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. + No se puede encontrar la entrada del registro para la ruta del archivo SQLUserInstance.dll.Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. - El valor del Registro contiene una ruta de acceso para el archivo SQLUserInstance.dll no válida. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. + El valor del registro contiene una ruta de acceso para el archivo SQLUserInstance.dll no válida. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. - No se puede cargar SQLUserInstance.dll desde la ubicación que se especificó en el Registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. + No se puede cargar SQLUserInstance.dll desde la ubicación que se especificó en el registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. - SQLUserInstance.dll no válido en la ubicación especificada en el Registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. + SQLUserInstance.dll no válido en la ubicación especificada en el registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. Error relacionado con la red o específico de la instancia mientras se establecía una conexión con el servidor SQL Server. No se encontró el servidor o éste no estaba accesible. Compruebe que el nombre de la instancia es correcto y que SQL Server está configurado para admitir conexiones remotas. @@ -3985,7 +3985,7 @@ Local Database Runtime: no se puede cargar SQLUserInstance.dll. - SQLUserInstance.dll no válido en la ubicación especificada en el Registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. + SQLUserInstance.dll no válido en la ubicación especificada en el registro. Compruebe que la característica de Local Database Runtime de SQL Server Express se ha instalado correctamente. No se puede obtener el mensaje de error de Local Database Runtime @@ -4617,4 +4617,13 @@ El parámetro “{0}” no puede tener la Dirección de salida ni InputOutput cuando EnableOptimizedParameterBinding está habilitado en el comando primario. + + Se agotó el tiempo de espera de la conexión al recuperar un token de acceso mediante el método de autenticación "{0}". Último error: {1}: {2} + + + Nombre de entidad de seguridad de servicio (SPN) del asociado de conmutación por error. + + + Nombre de entidad de seguridad de servicio (SPN) del servidor. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx index fbfa2141eb..485d4f88fa 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.fr.resx @@ -2872,7 +2872,7 @@ Les transactions globales ne sont pas activées pour cette base de données Azure SQL. Contactez le support d'Azure SQL Database pour obtenir de l'aide. - Le fichier System.Transactions.dll actuellement chargé ne prend pas en charge les transactions globales. Effectuez une mise à niveau vers .NET Framework 4.6.1 ou ultérieur. + Le fichier System.Transactions.dll actuellement chargé ne prend pas en charge les transactions globales. Effectuez une mise à niveau vers .NET Framework 4.6.2 ou ultérieur. Le traitement par lots des mises à jour n'est pas pris en charge sur la connexion du contexte. @@ -3850,7 +3850,7 @@ Erreur lors de la localisation de Server/Instance spécifié - Erreur d'obtention de la liste des protocoles activés à partir du Registre + Erreur d'obtention de la liste des protocoles activés à partir du registre Le serveur ne prend pas en charge le protocole demandé @@ -4617,4 +4617,13 @@ Le paramètre « {0} » ne peut pas avoir Direction Output ou InputOutput lorsque EnableOptimizedParameterBinding est activé sur la commande parente. + + La connexion a expiré lors de la récupération d’un jeton d’accès à l’aide de '{0}' méthode d’authentification. Dernière erreur : {1} : {2} + + + Le nom de principal du service (SPN) du partenaire de basculement. + + + Le nom de principal du service (SPN) du serveur. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx index 832d356758..1ea27733d9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.it.resx @@ -2872,7 +2872,7 @@ Le transazioni globali non sono abilitate per questo database SQL di Azure. Per assistenza, contattare il supporto del database SQL di Azure. - Il file System.Transactions.dll attualmente caricato non supporta le transazioni globali. Effettuare l'aggiornamento a .NET Framework 4.6.1 o versioni successive. + Il file System.Transactions.dll attualmente caricato non supporta le transazioni globali. Eseguire l'aggiornamento a .NET Framework 4.6.2 o versione successiva. L'esecuzione di batch di aggiornamenti non è supportata nella connessione contesto. @@ -4617,4 +4617,13 @@ Il parametro '{0}' non può includere Output o InputOutput come valore di Direction quando nel comando padre è abilitato EnableOptimizedParameterBinding. + + Timeout della connessione durante il recupero di un token di accesso tramite il metodo di autenticazione '{0}'. Ultimo errore: {1}: {2} + + + Nome principale dell'entità servizio (SPN) del partner di failover. + + + Nome principale dell'entità servizio (SPN) del server. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx index e65c5b4e8b..10916f4e40 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ja.resx @@ -2872,7 +2872,7 @@ この Azure SQL Database では、グローバル トランザクションは有効ではありません。Azure SQL Database サポートにお問い合わせください。 - 現在読み込まれている System.Transactions.dll では、グローバル トランザクションはサポートされていません。.NET Framework 4.6.1 またはそれ以降にアップグレードしてください。 + 現在読み込まれている System.Transactions.dll では、グローバル トランザクションはサポートされていません。.NET Framework 4.6.2 またはそれ以降にアップグレードしてください。 コンテキスト接続では、更新のバッチ処理はサポートされていません。 @@ -4617,4 +4617,13 @@ 親コマンドで EnableOptimizedParameterBinding が有効になっている場合、パラメーター '{0}' に Direction 出力または InputOutput は指定できません。 + + 認証方法 '{0}' によるアクセス トークンの取得中に接続がタイムアウトしました。前回のエラー: {1}: {2} + + + フェールオーバー パートナーのサービス プリンシパル名 (SPN)。 + + + サーバーのサービス プリンシパル名 (SPN)。 + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx index 2dff8da10f..02dd97c76d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ko.resx @@ -2872,7 +2872,7 @@ 이 Azure SQL Database에 대해 전역 트랙잭션을 사용하도록 설정되어 있지 않습니다. 도움이 필요하면 Azure SQL Database 지원 팀에 문의하세요. - 현재 로드된 System.Transactions.dll은 전역 트랜잭션을 지원하지 않습니다. .NET Framework 4.6.1 이상으로 업그레이드하세요. + 현재 로드된 System.Transactions.dll은 전역 트랜잭션을 지원하지 않습니다. .NET Framework 4.6.2 이상으로 업그레이드하세요. 컨텍스트 연결에서는 일괄 처리 업데이트가 지원되지 않습니다. @@ -3922,19 +3922,19 @@ Local Database Runtime 설치를 찾을 수 없습니다. SQL Server Express가 올바르게 설치되었고 로컬 데이터베이스 런타임 기능이 설정되었는지 확인하십시오. - 잘못된 Local Database Runtime 레지스트리 구성을 찾았습니다. SQL Server Express가 올바르게 설치되었는지 확인하십시오. + 잘못된 Local Database Runtime 레지스트리 구성을 찾았습니다. SQL Server Express가 올바르게 설치되었는지 확인하세요. - SQLUserInstance.dll 파일 경로에 대한 레지스트리 항목을 찾을 수 없습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하십시오. + SQLUserInstance.dll 파일 경로에 대한 레지스트리 항목을 찾을 수 없습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하세요. - 레지스트리 값에 잘못된 SQLUserInstance.dll 파일 경로가 들어 있습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하십시오. + 레지스트리 값에 잘못된 SQLUserInstance.dll 파일 경로가 들어 있습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하세요. - 레지스트리에 지정된 위치에서 SQLUserInstance.dll을 로드할 수 없습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하십시오. + 레지스트리에 지정된 위치에서 SQLUserInstance.dll을 로드할 수 없습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하세요. - 레지스트리에 지정된 위치에서 잘못된 SQLUserInstance.dll을 찾았습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하십시오. + 레지스트리에 지정된 위치에서 잘못된 SQLUserInstance.dll을 찾았습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하세요. SQL Server에 연결을 설정하는 중에 네트워크 관련 또는 인스턴스 관련 오류가 발생했습니다. 서버를 찾을 수 없거나 액세스할 수 없습니다. 인스턴스 이름이 올바르고 SQL Server가 원격 연결을 허용하도록 구성되어 있는지 확인하십시오. @@ -3985,7 +3985,7 @@ Local Database Runtime: SQLUserInstance.dll을 로드할 수 없습니다. - 레지스트리에 지정된 위치에서 잘못된 SQLUserInstance.dll을 찾았습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하십시오. + 레지스트리에 지정된 위치에서 잘못된 SQLUserInstance.dll을 찾았습니다. SQL Server Express의 Local Database Runtime 기능이 올바르게 설치되었는지 확인하세요. Local Database Runtime 오류 메시지를 가져올 수 없음 @@ -4617,4 +4617,13 @@ 부모 명령에서 EnableOptimizedParameterBinding을 사용하는 경우 매개 변수 '{0}'에는 Direction Output 또는 InputOutput을 사용할 수 없습니다. + + '{0}' 인증 방법을 사용하여 액세스 토큰을 검색하는 동안 연결 시간이 초과되었습니다. 마지막 오류: {1}: {2} + + + 장애 조치(failover) 파트너의 SPN(서비스 사용자 이름)입니다. + + + 서버의 SPN(서비스 사용자 이름)입니다. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx index eed81de659..0d181032ba 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.pt-BR.resx @@ -2872,7 +2872,7 @@ As Transações Globais não estão habilitadas para esse Banco de Dados SQL do Azure. Entre em contato com o suporte do Banco de Dados SQL do Azure para obter assistência. - O System.Transactions.dll carregado no momento não dá suporte a Transações Globais. Atualize para o .NET Framework 4.6.1 ou posterior. + O System.Transactions.dll carregado no momento não dá suporte a Transações Globais. Atualize para o .NET Framework 4.6.2 ou posterior. Não há suporte para as atualizações em lote na conexão de contexto. @@ -4617,4 +4617,13 @@ O parâmetro '{0}' não pode ter a saída de direção ou InputOutput quando EnableOptimizedParameterBinding está habilitado no comando pai. + + A conexão expirou ao recuperar um token de acesso usando o método de autenticação '{0}'. Último erro: {1}: {2} + + + O nome da entidade de serviço (SPN) do parceiro de failover. + + + O nome da entidade de serviço (SPN) do servidor. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx index acf6a6beff..66683219cc 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx @@ -2872,7 +2872,7 @@ Global Transactions are not enabled for this Azure SQL Database. Please contact Azure SQL Database support for assistance. - The currently loaded System.Transactions.dll does not support Global Transactions. Please upgrade to .NET Framework 4.6.1 or later. + The currently loaded System.Transactions.dll does not support Global Transactions. Please upgrade to .NET Framework 4.6.2 or later. Batching updates is not supported on the context connection. @@ -4617,4 +4617,13 @@ Parameter '{0}' cannot have Direction Output or InputOutput when EnableOptimizedParameterBinding is enabled on the parent command. + + Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2} + + + The service principal name (SPN) of the failover partner. + + + The service principal name (SPN) of the server. + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx index 97a4a9acef..511fdd00ef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.ru.resx @@ -2872,7 +2872,7 @@ Для этой базы данных SQL Azure глобальные транзакции не включены. Обратитесь за помощью в службу поддержки по базам данных SQL Azure. - Загруженная библиотека System.Transactions.dll не поддерживает глобальные транзакции. Перейдите на .NET Framework 4.6.1 или более позднюю версию. + Загруженная библиотека System.Transactions.dll не поддерживает глобальные транзакции. Перейдите на .NET Framework 4.6.2 или более позднюю версию. Группировка обновлений для контекстного подключения не поддерживается. @@ -4617,4 +4617,13 @@ У параметра "{0}" не может быть направления вывода или InputOutput, если EnableOptimizedParameterBinding включен в родительской команде. + + Истекло время ожидания подключения при получении маркера доступа с помощью метода проверки подлинности "{0}". Последняя ошибка: {1}: {2} + + + Имя субъекта-службы (SPN) партнера по обеспечению отработки отказа. + + + Имя субъекта-службы (SPN) сервера. + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx index 691824651b..6e2c3b212d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hans.resx @@ -2872,7 +2872,7 @@ 尚未为此 Azure SQL 数据库启用全局事务。若要获取帮助,请联系 Azure SQL 数据库支持人员。 - 当前加载的 System.Transactions.dll 不支持全局事务。请升级到 .NET Framework 4.6.1 或更高版本。 + 当前加载的 System.Transactions.dll 不支持全局事务。请升级到 .NET Framework 4.6.2 或更高版本。 在上下文连接上不支持批处理更新。 @@ -4617,4 +4617,13 @@ 当在父命令上启用 EnableOptimizedParameterBinding 时,参数“{0}”不能具有 Direction Output 或 InputOutput。 + + 使用“{0}”身份验证方法检索访问令牌时连接超时。最后一个错误: {1}: {2} + + + 故障转移合作伙伴的服务主体名称(SPN)。 + + + 服务器的服务主体名称(SPN)。 + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx index 73b2ee5153..efc451484a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx +++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.zh-Hant.resx @@ -2872,7 +2872,7 @@ 此 Azure SQL Database 未啟用全域交易。請連絡 Azure SQL Database 支援以取得協助。 - 目前載入的 System.Transactions.dll 不支援全域交易。請升級為 .NET Framework 4.6.1 或更新版本。 + 目前載入的 System.Transactions.dll 不支援全域交易。請升級為 .NET Framework 4.6.2 或更新版本。 內容連接上並不支援批次更新。 @@ -4617,4 +4617,13 @@ 在父命令上啟用 EnableOptimizedParameterBinding 時,參數 '{0}' 不能具有方向輸出或 InputOutput。 + + 使用 '{0}' 驗證方法擷取存取權杖時已逾時。上次錯誤: {1}: {2} + + + 容錯移轉夥伴的服務主體名稱 (SPN)。 + + + 伺服器的服務主體名稱 (SPN)。 + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Unix.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Unix.cs new file mode 100644 index 0000000000..8b84feecfc --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Unix.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.Common +{ + /// + /// The class ADP defines the exceptions that are specific to the Adapters. + /// The class contains functions that take the proper informational variables and then construct + /// the appropriate exception with an error string obtained from the resource framework. + /// The exception is then returned to the caller, so that the caller may then throw from its + /// location so that the catcher of the exception will have the appropriate call stack. + /// This class is used so that there will be compile time checking of error messages. + /// The resource Framework.txt will ensure proper string text based on the appropriate locale. + /// + internal static partial class ADP + { + internal static object LocalMachineRegistryValue(string subkey, string queryvalue) + { + // No registry in non-Windows environments + return null; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs new file mode 100644 index 0000000000..c9d0f8d91a --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.Windows.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using System.Security.Permissions; +using Microsoft.Win32; + +namespace Microsoft.Data.Common +{ + /// + /// The class ADP defines the exceptions that are specific to the Adapters. + /// The class contains functions that take the proper informational variables and then construct + /// the appropriate exception with an error string obtained from the resource framework. + /// The exception is then returned to the caller, so that the caller may then throw from its + /// location so that the catcher of the exception will have the appropriate call stack. + /// This class is used so that there will be compile time checking of error messages. + /// The resource Framework.txt will ensure proper string text based on the appropriate locale. + /// + internal static partial class ADP + { + [ResourceExposure(ResourceScope.Machine)] + [ResourceConsumption(ResourceScope.Machine)] + internal static object LocalMachineRegistryValue(string subkey, string queryvalue) + { // MDAC 77697 + (new RegistryPermission(RegistryPermissionAccess.Read, "HKEY_LOCAL_MACHINE\\" + subkey)).Assert(); // MDAC 62028 + try + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(subkey, false)) + { + return key?.GetValue(queryvalue); + } + } + catch (SecurityException e) + { + // Even though we assert permission - it's possible there are + // ACL's on registry that cause SecurityException to be thrown. + ADP.TraceExceptionWithoutRethrow(e); + return null; + } + finally + { + RegistryPermission.RevertAssert(); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs index f7e3715ccc..1866aa7fb3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs @@ -11,9 +11,7 @@ using System.Globalization; using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; + using System.Security; using System.Security.Permissions; using System.Text; @@ -21,9 +19,17 @@ using System.Threading.Tasks; using System.Transactions; using Microsoft.Data.SqlClient; -using Microsoft.Data.SqlClient.Server; -using Microsoft.Win32; using IsolationLevel = System.Data.IsolationLevel; +using Microsoft.Identity.Client; +using Microsoft.SqlServer.Server; + +#if NETFRAMEWORK +using Microsoft.Win32; +using System.Reflection; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +#endif namespace Microsoft.Data.Common { @@ -36,7 +42,7 @@ namespace Microsoft.Data.Common /// This class is used so that there will be compile time checking of error messages. /// The resource Framework.txt will ensure proper string text based on the appropriate locale. /// - internal static class ADP + internal static partial class ADP { // NOTE: Initializing a Task in SQL CLR requires the "UNSAFE" permission set (http://msdn.microsoft.com/en-us/library/ms172338.aspx) // Therefore we are lazily initializing these Tasks to avoid forcing customers to use the "UNSAFE" set when they are actually using no Async features @@ -57,6 +63,26 @@ internal static class ADP /// internal const int MaxBufferAccessTokenExpiry = 600; + #region UDT +#if NETFRAMEWORK + private static readonly MethodInfo s_method = typeof(InvalidUdtException).GetMethod("Create", BindingFlags.NonPublic | BindingFlags.Static); +#endif + /// + /// Calls "InvalidUdtException.Create" method when an invalid UDT occurs. + /// + internal static InvalidUdtException CreateInvalidUdtException(Type udtType, string resourceReasonName) + { + InvalidUdtException e = +#if NETFRAMEWORK + (InvalidUdtException)s_method.Invoke(null, new object[] { udtType, resourceReasonName }); + ADP.TraceExceptionAsReturnValue(e); +#else + InvalidUdtException.Create(udtType, resourceReasonName); +#endif + return e; + } + #endregion + static private void TraceException(string trace, Exception e) { Debug.Assert(null != e, "TraceException: null Exception"); @@ -101,7 +127,7 @@ internal static Exception ExceptionWithStackTrace(Exception e) } } - #region COM+ exceptions +#region COM+ exceptions internal static ArgumentException Argument(string error) { ArgumentException e = new(error); @@ -188,9 +214,9 @@ internal static OverflowException Overflow(string error, Exception inner) return e; } - internal static TimeoutException TimeoutException(string error) + internal static TimeoutException TimeoutException(string error, Exception inner = null) { - TimeoutException e = new(error); + TimeoutException e = new(error, inner); TraceExceptionAsReturnValue(e); return e; } @@ -300,9 +326,9 @@ internal static ArgumentOutOfRangeException ArgumentOutOfRange(string message, s TraceExceptionAsReturnValue(e); return e; } - #endregion +#endregion - #region Helper Functions +#region Helper Functions internal static ArgumentOutOfRangeException NotSupportedEnumerationValue(Type type, string value, string method) => ArgumentOutOfRange(StringsHelper.GetString(Strings.ADP_NotSupportedEnumerationValue, type.Name, value, method), type.Name); @@ -390,9 +416,36 @@ internal static ArgumentException InvalidArgumentLength(string argumentName, int => Argument(StringsHelper.GetString(Strings.ADP_InvalidArgumentLength, argumentName, limit)); internal static ArgumentException MustBeReadOnly(string argumentName) => Argument(StringsHelper.GetString(Strings.ADP_MustBeReadOnly, argumentName)); - #endregion - #region CommandBuilder, Command, BulkCopy + internal static Exception CreateSqlException(MsalException msalException, SqlConnectionString connectionOptions, SqlInternalConnectionTds sender, string username) + { + // Error[0] + SqlErrorCollection sqlErs = new(); + + sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, + connectionOptions.DataSource, + StringsHelper.GetString(Strings.SQL_MSALFailure, username, connectionOptions.Authentication.ToString("G")), + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + + // Error[1] + string errorMessage1 = StringsHelper.GetString(Strings.SQL_MSALInnerException, msalException.ErrorCode); + sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, + connectionOptions.DataSource, errorMessage1, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + + // Error[2] + if (!string.IsNullOrEmpty(msalException.Message)) + { + sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, + connectionOptions.DataSource, msalException.Message, + ActiveDirectoryAuthentication.MSALGetAccessTokenFunctionName, 0)); + } + return SqlException.CreateException(sqlErs, "", sender); + } + +#endregion + +#region CommandBuilder, Command, BulkCopy /// /// This allows the caller to determine if it is an error or not for the quotedString to not be quoted /// @@ -660,13 +713,26 @@ internal static Version GetAssemblyVersion() } + private const string ONDEMAND_PREFIX = "-ondemand"; + private const string AZURE_SYNAPSE = "-ondemand.sql.azuresynapse."; + + internal static bool IsAzureSynapseOnDemandEndpoint(string dataSource) + { + return IsEndpoint(dataSource, ONDEMAND_PREFIX) || dataSource.Contains(AZURE_SYNAPSE); + } + internal static readonly string[] s_azureSqlServerEndpoints = { StringsHelper.GetString(Strings.AZURESQL_GenericEndpoint), StringsHelper.GetString(Strings.AZURESQL_GermanEndpoint), StringsHelper.GetString(Strings.AZURESQL_UsGovEndpoint), StringsHelper.GetString(Strings.AZURESQL_ChinaEndpoint)}; - // This method assumes dataSource parameter is in TCP connection string format. internal static bool IsAzureSqlServerEndpoint(string dataSource) + { + return IsEndpoint(dataSource, null); + } + + // This method assumes dataSource parameter is in TCP connection string format. + private static bool IsEndpoint(string dataSource, string prefix) { int length = dataSource.Length; // remove server port @@ -689,10 +755,10 @@ internal static bool IsAzureSqlServerEndpoint(string dataSource) length -= 1; } - // check if servername end with any azure endpoints + // check if servername ends with any endpoints for (int index = 0; index < s_azureSqlServerEndpoints.Length; index++) { - string endpoint = s_azureSqlServerEndpoints[index]; + string endpoint = string.IsNullOrEmpty(prefix) ? s_azureSqlServerEndpoints[index] : prefix + s_azureSqlServerEndpoints[index]; if (length > endpoint.Length) { if (string.Compare(dataSource, length - endpoint.Length, endpoint, 0, endpoint.Length, StringComparison.OrdinalIgnoreCase) == 0) @@ -725,9 +791,9 @@ internal static ArgumentException InvalidPrefixSuffix() TraceExceptionAsReturnValue(e); return e; } - #endregion +#endregion - #region DbConnectionOptions, DataAccess +#region DbConnectionOptions, DataAccess internal static ArgumentException ConnectionStringSyntax(int index) => Argument(StringsHelper.GetString(Strings.ADP_ConnectionStringSyntax, index)); internal static ArgumentException KeywordNotSupported(string keyword) => Argument(StringsHelper.GetString(Strings.ADP_KeywordNotSupported, keyword)); @@ -758,16 +824,16 @@ internal static IndexOutOfRangeException CollectionIndexString(Type itemType, st => IndexOutOfRange(StringsHelper.GetString(Strings.ADP_CollectionIndexString, itemType.Name, propertyName, propertyValue, collection.Name)); internal static InvalidCastException CollectionInvalidType(Type collection, Type itemType, object invalidValue) - => InvalidCast(StringsHelper.GetString(Strings.ADP_CollectionInvalidType, collection.Name, itemType.Name, invalidValue.GetType().Name)); + => InvalidCast(StringsHelper.GetString(Strings.ADP_CollectionInvalidType, collection.Name, itemType.FullName, invalidValue.GetType().FullName)); internal static ArgumentException ConvertFailed(Type fromType, Type toType, Exception innerException) => ADP.Argument(StringsHelper.GetString(Strings.SqlConvert_ConvertFailed, fromType.FullName, toType.FullName), innerException); internal static ArgumentException InvalidMinMaxPoolSizeValues() => ADP.Argument(StringsHelper.GetString(Strings.ADP_InvalidMinMaxPoolSizeValues)); - #endregion +#endregion - #region DbConnection +#region DbConnection private static string ConnectionStateMsg(ConnectionState state) { // MDAC 82165, if the ConnectionState enum to msg the localization looks weird return state switch @@ -790,25 +856,25 @@ internal static NotImplementedException MethodNotImplemented([CallerMemberName] TraceExceptionAsReturnValue(e); return e; } - #endregion +#endregion - #region Stream +#region Stream internal static Exception StreamClosed([CallerMemberName] string method = "") => InvalidOperation(StringsHelper.GetString(Strings.ADP_StreamClosed, method)); static internal Exception InvalidSeekOrigin(string parameterName) => ArgumentOutOfRange(StringsHelper.GetString(Strings.ADP_InvalidSeekOrigin), parameterName); internal static IOException ErrorReadingFromStream(Exception internalException) => IO(StringsHelper.GetString(Strings.SqlMisc_StreamErrorMessage), internalException); - #endregion +#endregion - #region Generic Data Provider Collection +#region Generic Data Provider Collection internal static ArgumentException ParametersIsNotParent(Type parameterType, ICollection collection) => Argument(StringsHelper.GetString(Strings.ADP_CollectionIsNotParent, parameterType.Name, collection.GetType().Name)); internal static ArgumentException ParametersIsParent(Type parameterType, ICollection collection) => Argument(StringsHelper.GetString(Strings.ADP_CollectionIsNotParent, parameterType.Name, collection.GetType().Name)); - #endregion +#endregion - #region ConnectionUtil +#region ConnectionUtil internal enum InternalErrorCode { UnpooledObjectHasOwner = 0, @@ -879,9 +945,9 @@ internal static Exception InternalConnectionError(ConnectionError internalError) internal static Exception InvalidConnectRetryCountValue() => Argument(StringsHelper.GetString(Strings.SQLCR_InvalidConnectRetryCountValue)); internal static Exception InvalidConnectRetryIntervalValue() => Argument(StringsHelper.GetString(Strings.SQLCR_InvalidConnectRetryIntervalValue)); - #endregion +#endregion - #region DbDataReader +#region DbDataReader internal static Exception DataReaderClosed([CallerMemberName] string method = "") => InvalidOperation(StringsHelper.GetString(Strings.ADP_DataReaderClosed, method)); @@ -923,9 +989,9 @@ internal static Exception InvalidXmlMissingColumn(string collectionName, string => Argument(StringsHelper.GetString(Strings.MDF_InvalidXmlMissingColumn, collectionName, columnName)); internal static InvalidOperationException AsyncOperationPending() => InvalidOperation(StringsHelper.GetString(Strings.ADP_PendingAsyncOperation)); - #endregion +#endregion - #region IDbCommand +#region IDbCommand // IDbCommand.CommandType static internal ArgumentOutOfRangeException InvalidCommandType(CommandType value) { @@ -994,9 +1060,9 @@ internal static Exception DeriveParametersNotSupported(IDbCommand value) => DataAdapter(StringsHelper.GetString(Strings.ADP_DeriveParametersNotSupported, value.GetType().Name, value.CommandType.ToString())); internal static Exception NoStoredProcedureExists(string sproc) => InvalidOperation(StringsHelper.GetString(Strings.ADP_NoStoredProcedureExists, sproc)); - #endregion +#endregion - #region DbMetaDataFactory +#region DbMetaDataFactory internal static Exception DataTableDoesNotExist(string collectionName) => Argument(StringsHelper.GetString(Strings.MDF_DataTableDoesNotExist, collectionName)); @@ -1067,17 +1133,17 @@ internal static Exception UndefinedPopulationMechanism(string populationMechanis #else => throw new NotImplementedException(); #endif - #endregion +#endregion - #region DbConnectionPool and related +#region DbConnectionPool and related internal static Exception PooledOpenTimeout() => ADP.InvalidOperation(StringsHelper.GetString(Strings.ADP_PooledOpenTimeout)); internal static Exception NonPooledOpenTimeout() => ADP.TimeoutException(StringsHelper.GetString(Strings.ADP_NonPooledOpenTimeout)); - #endregion +#endregion - #region DbProviderException +#region DbProviderException internal static InvalidOperationException TransactionConnectionMismatch() => Provider(StringsHelper.GetString(Strings.ADP_TransactionConnectionMismatch)); @@ -1086,18 +1152,18 @@ internal static InvalidOperationException TransactionRequired(string method) internal static InvalidOperationException TransactionCompletedButNotDisposed() => Provider(StringsHelper.GetString(Strings.ADP_TransactionCompletedButNotDisposed)); - #endregion +#endregion - #region SqlMetaData, SqlTypes +#region SqlMetaData, SqlTypes internal static Exception InvalidMetaDataValue() => ADP.Argument(StringsHelper.GetString(Strings.ADP_InvalidMetaDataValue)); internal static InvalidOperationException NonSequentialColumnAccess(int badCol, int currCol) => InvalidOperation(StringsHelper.GetString(Strings.ADP_NonSequentialColumnAccess, badCol.ToString(CultureInfo.InvariantCulture), currCol.ToString(CultureInfo.InvariantCulture))); - #endregion +#endregion - #region IDataParameter +#region IDataParameter internal static ArgumentException InvalidDataType(TypeCode typecode) => Argument(StringsHelper.GetString(Strings.ADP_InvalidDataType, typecode.ToString())); internal static ArgumentException UnknownDataType(Type dataType) => Argument(StringsHelper.GetString(Strings.ADP_UnknownDataType, dataType.FullName)); @@ -1153,9 +1219,9 @@ internal static Exception ParameterConversionFailed(object value, Type destType, TraceExceptionAsReturnValue(e); return e; } - #endregion +#endregion - #region IDataParameterCollection +#region IDataParameterCollection internal static Exception ParametersMappingIndex(int index, DbParameterCollection collection) => CollectionIndexInt32(index, collection.GetType(), collection.Count); internal static Exception ParametersSourceIndex(string parameterName, DbParameterCollection collection, Type parameterType) @@ -1166,16 +1232,16 @@ internal static Exception ParameterNull(string parameter, DbParameterCollection internal static Exception InvalidParameterType(DbParameterCollection collection, Type parameterType, object invalidValue) => CollectionInvalidType(collection.GetType(), parameterType, invalidValue); - #endregion +#endregion - #region IDbTransaction +#region IDbTransaction internal static Exception ParallelTransactionsNotSupported(DbConnection obj) => InvalidOperation(StringsHelper.GetString(Strings.ADP_ParallelTransactionsNotSupported, obj.GetType().Name)); internal static Exception TransactionZombied(DbTransaction obj) => InvalidOperation(StringsHelper.GetString(Strings.ADP_TransactionZombied, obj.GetType().Name)); - #endregion +#endregion - #region DbProviderConfigurationHandler +#region DbProviderConfigurationHandler internal static InvalidOperationException InvalidMixedUsageOfSecureAndClearCredential() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfSecureAndClearCredential)); @@ -1199,10 +1265,12 @@ static internal InvalidOperationException InvalidMixedUsageOfAccessTokenAndAuthe static internal Exception InvalidMixedUsageOfCredentialAndAccessToken() => InvalidOperation(StringsHelper.GetString(Strings.ADP_InvalidMixedUsageOfCredentialAndAccessToken)); - #endregion +#endregion + internal static bool IsEmpty(string str) => string.IsNullOrEmpty(str); + internal static readonly IntPtr s_ptrZero = IntPtr.Zero; #if NETFRAMEWORK - #region netfx project only +#region netfx project only internal static Task CreatedTaskWithException(Exception ex) { TaskCompletionSource completion = new(); @@ -1355,7 +1423,6 @@ internal static InvalidOperationException ComputerNameEx(int lastError) internal const float FailoverTimeoutStepForTnir = 0.125F; // Fraction of timeout to use in case of Transparent Network IP resolution. internal const int MinimumTimeoutForTnirMs = 500; // The first login attempt in Transparent network IP Resolution - internal static readonly IntPtr s_ptrZero = IntPtr.Zero; // IntPtr.Zero internal static readonly int s_ptrSize = IntPtr.Size; internal static readonly IntPtr s_invalidPtr = new(-1); // use for INVALID_HANDLE @@ -1410,31 +1477,6 @@ internal static string GetComputerNameDnsFullyQualified() return value; } - [ResourceExposure(ResourceScope.Machine)] - [ResourceConsumption(ResourceScope.Machine)] - internal static object LocalMachineRegistryValue(string subkey, string queryvalue) - { // MDAC 77697 - (new RegistryPermission(RegistryPermissionAccess.Read, "HKEY_LOCAL_MACHINE\\" + subkey)).Assert(); // MDAC 62028 - try - { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(subkey, false)) - { - return key?.GetValue(queryvalue); - } - } - catch (SecurityException e) - { - // Even though we assert permission - it's possible there are - // ACL's on registry that cause SecurityException to be thrown. - ADP.TraceExceptionWithoutRethrow(e); - return null; - } - finally - { - RegistryPermission.RevertAssert(); - } - } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static IntPtr IntPtrOffset(IntPtr pbase, int offset) { @@ -1446,10 +1488,9 @@ internal static IntPtr IntPtrOffset(IntPtr pbase, int offset) return (IntPtr)checked(pbase.ToInt64() + offset); } - internal static bool IsEmpty(string str) => string.IsNullOrEmpty(str); - #endregion +#endregion #else - #region netcore project only +#region netcore project only internal static Timer UnsafeCreateTimer(TimerCallback callback, object state, int dueTime, int period) { // Don't capture the current ExecutionContext and its AsyncLocals onto @@ -1498,6 +1539,9 @@ internal static ArgumentOutOfRangeException InvalidIsolationLevel(IsolationLevel return InvalidEnumerationValue(typeof(IsolationLevel), (int)value); } + // ConnectionUtil + internal static Exception IncorrectPhysicalConnectionType() => new ArgumentException(StringsHelper.GetString(StringsHelper.SNI_IncorrectPhysicalConnectionType)); + // IDataParameter.Direction internal static ArgumentOutOfRangeException InvalidParameterDirection(ParameterDirection value) { @@ -1520,7 +1564,7 @@ internal static ArgumentOutOfRangeException InvalidParameterDirection(ParameterD // internal static Exception InvalidCommandTimeout(int value, [CallerMemberName] string property = "") => Argument(StringsHelper.GetString(Strings.ADP_InvalidCommandTimeout, value.ToString(CultureInfo.InvariantCulture)), property); - #endregion +#endregion #endif } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs index de5d1cb9e5..65e425590e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionOptions.Common.cs @@ -25,6 +25,7 @@ private static class KEY internal const string Password = DbConnectionStringKeywords.Password; internal const string Persist_Security_Info = DbConnectionStringKeywords.PersistSecurityInfo; internal const string User_ID = DbConnectionStringKeywords.UserID; + internal const string Encrypt = DbConnectionStringKeywords.Encrypt; } // known connection string common synonyms @@ -238,7 +239,7 @@ internal static bool ConvertValueToBooleanInternal(string keyName, string string } } - private static bool CompareInsensitiveInvariant(string strvalue, string strconst) + private static bool CompareInsensitiveInvariant(string strvalue, string strconst) => (0 == StringComparer.OrdinalIgnoreCase.Compare(strvalue, strconst)); [System.Diagnostics.Conditional("DEBUG")] @@ -725,7 +726,7 @@ internal NameValuePair ReplacePasswordPwd(out string constr, bool fakePassword) StringBuilder builder = new StringBuilder(_usersConnectionString.Length); for (NameValuePair current = _keyChain; null != current; current = current.Next) { - if(!string.Equals(KEY.Password, current.Name, StringComparison.InvariantCultureIgnoreCase) && + if (!string.Equals(KEY.Password, current.Name, StringComparison.InvariantCultureIgnoreCase) && !string.Equals(SYNONYM.Pwd, current.Name, StringComparison.InvariantCultureIgnoreCase)) { builder.Append(_usersConnectionString, copyPosition, current.Length); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs index aad002c116..ee6fafa0f0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/DbConnectionStringCommon.cs @@ -715,13 +715,11 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec result = SqlConnectionAttestationProtocol.AAS; return true; } -#if ENCLAVE_SIMULATOR - else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, nameof(SqlConnectionAttestationProtocol.SIM))) + else if (StringComparer.InvariantCultureIgnoreCase.Equals(value, nameof(SqlConnectionAttestationProtocol.None))) { - result = SqlConnectionAttestationProtocol.SIM; + result = SqlConnectionAttestationProtocol.None; return true; } -#endif else { result = DbConnectionStringDefaults.AttestationProtocol; @@ -731,18 +729,11 @@ internal static bool TryConvertToAttestationProtocol(string value, out SqlConnec internal static bool IsValidAttestationProtocol(SqlConnectionAttestationProtocol value) { -#if ENCLAVE_SIMULATOR Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 4, "SqlConnectionAttestationProtocol enum has changed, update needed"); return value == SqlConnectionAttestationProtocol.NotSpecified || value == SqlConnectionAttestationProtocol.HGS || value == SqlConnectionAttestationProtocol.AAS - || value == SqlConnectionAttestationProtocol.SIM; -#else - Debug.Assert(Enum.GetNames(typeof(SqlConnectionAttestationProtocol)).Length == 3, "SqlConnectionAttestationProtocol enum has changed, update needed"); - return value == SqlConnectionAttestationProtocol.NotSpecified - || value == SqlConnectionAttestationProtocol.HGS - || value == SqlConnectionAttestationProtocol.AAS; -#endif + || value == SqlConnectionAttestationProtocol.None; } internal static string AttestationProtocolToString(SqlConnectionAttestationProtocol value) @@ -753,9 +744,7 @@ internal static string AttestationProtocolToString(SqlConnectionAttestationProto { SqlConnectionAttestationProtocol.AAS => nameof(SqlConnectionAttestationProtocol.AAS), SqlConnectionAttestationProtocol.HGS => nameof(SqlConnectionAttestationProtocol.HGS), -#if ENCLAVE_SIMULATOR - SqlConnectionAttestationProtocol.SIM => nameof(SqlConnectionAttestationProtocol.SIM), -#endif + SqlConnectionAttestationProtocol.None => nameof(SqlConnectionAttestationProtocol.None), _ => null }; } @@ -821,6 +810,20 @@ internal static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(st } } + internal static SqlConnectionEncryptOption ConvertToSqlConnectionEncryptOption(string keyword, object value) + { + if (value is null) + { + return DbConnectionStringDefaults.Encrypt; + } + else if (value is string sValue) + { + return SqlConnectionEncryptOption.Parse(sValue); + } + + throw ADP.InvalidConnectionOptionValue(keyword); + } + #endregion #region <> @@ -958,7 +961,8 @@ internal static class DbConnectionStringDefaults #endif internal const string CurrentLanguage = ""; internal const string DataSource = ""; - internal const bool Encrypt = true; + internal static readonly SqlConnectionEncryptOption Encrypt = SqlConnectionEncryptOption.Mandatory; + internal const string HostNameInCertificate = ""; internal const bool Enlist = true; internal const string FailoverPartner = ""; internal const string InitialCatalog = ""; @@ -987,6 +991,8 @@ internal static class DbConnectionStringDefaults internal const SqlConnectionAttestationProtocol AttestationProtocol = SqlConnectionAttestationProtocol.NotSpecified; internal const SqlConnectionIPAddressPreference IPAddressPreference = SqlConnectionIPAddressPreference.IPv4First; internal const PoolBlockingPeriod PoolBlockingPeriod = SqlClient.PoolBlockingPeriod.Auto; + internal const string ServerSPN = ""; + internal const string FailoverPartnerSPN = ""; } internal static class DbConnectionStringKeywords @@ -1021,6 +1027,7 @@ internal static class DbConnectionStringKeywords internal const string ContextConnection = "Context Connection"; internal const string CurrentLanguage = "Current Language"; internal const string Encrypt = "Encrypt"; + internal const string HostNameInCertificate = "Host Name In Certificate"; internal const string FailoverPartner = "Failover Partner"; internal const string InitialCatalog = "Initial Catalog"; internal const string MultipleActiveResultSets = "Multiple Active Result Sets"; @@ -1040,6 +1047,8 @@ internal static class DbConnectionStringKeywords internal const string EnclaveAttestationUrl = "Enclave Attestation Url"; internal const string AttestationProtocol = "Attestation Protocol"; internal const string IPAddressPreference = "IP Address Preference"; + internal const string ServerSPN = "Server SPN"; + internal const string FailoverPartnerSPN = "Failover Partner SPN"; // common keywords (OleDb, OracleClient, SqlClient) internal const string DataSource = "Data Source"; @@ -1076,6 +1085,9 @@ internal static class DbConnectionStringSynonyms internal const string EXTENDEDPROPERTIES = "extended properties"; internal const string INITIALFILENAME = "initial file name"; + // internal const string HostNameInCertificate = HOSTNAMEINCERTIFICATE; + internal const string HOSTNAMEINCERTIFICATE = "hostnameincertificate"; + //internal const string ConnectTimeout = CONNECTIONTIMEOUT+","+TIMEOUT; internal const string CONNECTIONTIMEOUT = "connection timeout"; internal const string TIMEOUT = "timeout"; @@ -1133,5 +1145,9 @@ internal static class DbConnectionStringSynonyms //internal const string WorkstationID = WSID; internal const string WSID = "wsid"; + + //internal const string server SPNs + internal const string ServerSPN = "ServerSPN"; + internal const string FailoverPartnerSPN = "FailoverPartnerSPN"; } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs similarity index 92% rename from src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 51109fc388..7568340594 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -6,7 +6,7 @@ using Microsoft.Data.Common; using Microsoft.Data.SqlClient; using System.Collections.Concurrent; -using System.Data.Common; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -52,6 +52,9 @@ sealed internal class DbConnectionPoolGroup internal DbConnectionPoolGroup(DbConnectionOptions connectionOptions, DbConnectionPoolKey key, DbConnectionPoolGroupOptions poolGroupOptions) { Debug.Assert(null != connectionOptions, "null connection options"); +#if NETFRAMEWORK + Debug.Assert(null == poolGroupOptions || ADP.s_isWindowsNT, "should not have pooling options on Win9x"); +#endif _connectionOptions = connectionOptions; _poolKey = key; @@ -123,12 +126,15 @@ internal int Clear() // Then, if a new collection was created, release the pools from the old collection if (oldPoolCollection != null) { - foreach (var entry in oldPoolCollection) + foreach (KeyValuePair entry in oldPoolCollection) { DbConnectionPool pool = entry.Value; if (pool != null) { DbConnectionFactory connectionFactory = pool.ConnectionFactory; +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); +#endif connectionFactory.QueuePoolForRelease(pool, true); } } @@ -149,6 +155,10 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor DbConnectionPool pool = null; if (null != _poolGroupOptions) { +#if NETFRAMEWORK + Debug.Assert(ADP.s_isWindowsNT, "should not be pooling on Win9x"); +#endif + DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; if (_poolGroupOptions.PoolByIdentity) @@ -176,8 +186,8 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor // Did someone already add it to the list? if (!_poolCollection.TryGetValue(currentIdentity, out pool)) { - DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(this.ConnectionOptions); - DbConnectionPool newPool = new DbConnectionPool(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); + DbConnectionPoolProviderInfo connectionPoolProviderInfo = connectionFactory.CreateConnectionPoolProviderInfo(ConnectionOptions); + DbConnectionPool newPool = new(connectionFactory, this, currentIdentity, connectionPoolProviderInfo); if (MarkPoolGroupAsActive()) { @@ -188,6 +198,9 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor bool addResult = _poolCollection.TryAdd(currentIdentity, newPool); Debug.Assert(addResult, "No other pool with current identity should exist at this point"); SqlClientEventSource.Log.EnterActiveConnectionPool(); +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Increment(); +#endif pool = newPool; } else @@ -246,7 +259,7 @@ internal bool Prune() { var newPoolCollection = new ConcurrentDictionary(); - foreach (var entry in _poolCollection) + foreach (KeyValuePair entry in _poolCollection) { DbConnectionPool pool = entry.Value; if (pool != null) @@ -263,6 +276,9 @@ internal bool Prune() // pool into a list of pools to be released when they // are completely empty. DbConnectionFactory connectionFactory = pool.ConnectionFactory; +#if NETFRAMEWORK + connectionFactory.PerformanceCounters.NumberOfActiveConnectionPools.Decrement(); +#endif connectionFactory.QueuePoolForRelease(pool, false); } else diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs new file mode 100644 index 0000000000..83ce5085e7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.Windows.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System.Data; +using System.Data.Common; +using Microsoft.Data.SqlClient.Server; + +namespace Microsoft.Data.Sql +{ + /// + public sealed partial class SqlDataSourceEnumerator : DbDataSourceEnumerator + { + private partial DataTable GetDataSourcesInternal() + { +#if NETFRAMEWORK + return SqlDataSourceEnumeratorNativeHelper.GetDataSources(); +#else + return SqlClient.TdsParserStateObjectFactory.UseManagedSNI ? SqlDataSourceEnumeratorManagedHelper.GetDataSources() : SqlDataSourceEnumeratorNativeHelper.GetDataSources(); +#endif + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.cs new file mode 100644 index 0000000000..e8f7aac29c --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumerator.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System; +using System.Data; +using System.Data.Common; + +namespace Microsoft.Data.Sql +{ + /// + public sealed partial class SqlDataSourceEnumerator : DbDataSourceEnumerator + { + private static readonly Lazy s_singletonInstance = new(() => new SqlDataSourceEnumerator()); + + private SqlDataSourceEnumerator() : base(){} + + /// + public static SqlDataSourceEnumerator Instance => s_singletonInstance.Value; + + /// + override public DataTable GetDataSources() => GetDataSourcesInternal(); + + private partial DataTable GetDataSourcesInternal(); + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs new file mode 100644 index 0000000000..43be666e0d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorManagedHelper.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +using System.Collections.Generic; +using System.Data; +using Microsoft.Data.Sql; + +namespace Microsoft.Data.SqlClient.Server +{ + /// + /// Provides a mechanism for enumerating all available instances of SQL Server within the local network + /// + internal static class SqlDataSourceEnumeratorManagedHelper + { + /// + /// Provides a mechanism for enumerating all available instances of SQL Server within the local network. + /// + /// DataTable with ServerName,InstanceName,IsClustered and Version + internal static DataTable GetDataSources() + { + // TODO: Implement multicast request besides the implemented broadcast request. + throw new System.NotImplementedException(StringsHelper.net_MethodNotImplementedException); + } + + private static DataTable ParseServerEnumString(string serverInstances) + { + DataTable dataTable = SqlDataSourceEnumeratorUtil.PrepareDataTable(); + DataRow dataRow; + + if (serverInstances.Length == 0) + { + return dataTable; + } + + string[] numOfServerInstances = serverInstances.Split(SqlDataSourceEnumeratorUtil.s_endOfServerInstanceDelimiter_Managed, System.StringSplitOptions.None); + SqlClientEventSource.Log.TryTraceEvent(" Number of recieved server instances are {2}", + nameof(SqlDataSourceEnumeratorManagedHelper), nameof(ParseServerEnumString), numOfServerInstances.Length); + + foreach (string currentServerInstance in numOfServerInstances) + { + Dictionary InstanceDetails = new(); + string[] delimitedKeyValues = currentServerInstance.Split(SqlDataSourceEnumeratorUtil.InstanceKeysDelimiter); + string currentKey = string.Empty; + + for (int keyvalue = 0; keyvalue < delimitedKeyValues.Length; keyvalue++) + { + if (keyvalue % 2 == 0) + { + currentKey = delimitedKeyValues[keyvalue]; + } + else if (currentKey != string.Empty) + { + InstanceDetails.Add(currentKey, delimitedKeyValues[keyvalue]); + } + } + + if (InstanceDetails.Count > 0) + { + dataRow = dataTable.NewRow(); + dataRow[0] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.ServerNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.ServerNameCol] : string.Empty; + dataRow[1] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.InstanceNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.InstanceNameCol] : string.Empty; + dataRow[2] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.IsClusteredCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.IsClusteredCol] : string.Empty; + dataRow[3] = InstanceDetails.ContainsKey(SqlDataSourceEnumeratorUtil.VersionNameCol) == true ? + InstanceDetails[SqlDataSourceEnumeratorUtil.VersionNameCol] : string.Empty; + + dataTable.Rows.Add(dataRow); + } + } + return dataTable.SetColumnsReadOnly(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs new file mode 100644 index 0000000000..f6ebfc4b8f --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorNativeHelper.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Security; +using System.Text; +using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; +using static Microsoft.Data.Sql.SqlDataSourceEnumeratorUtil; + +namespace Microsoft.Data.Sql +{ + /// + /// Provides a mechanism for enumerating all available instances of SQL Server within the local network + /// + internal static class SqlDataSourceEnumeratorNativeHelper + { + /// + /// Retrieves a DataTable containing information about all visible SQL Server instances + /// + /// + internal static DataTable GetDataSources() + { + (new NamedPermissionSet("FullTrust")).Demand(); // SQLBUDT 244304 + char[] buffer = null; + StringBuilder strbldr = new(); + + int bufferSize = 1024; + int readLength = 0; + buffer = new char[bufferSize]; + bool more = true; + bool failure = false; + IntPtr handle = ADP.s_ptrZero; + + RuntimeHelpers.PrepareConstrainedRegions(); + try + { + long s_timeoutTime = TdsParserStaticMethods.GetTimeoutSeconds(ADP.DefaultCommandTimeout); + RuntimeHelpers.PrepareConstrainedRegions(); + try + { } + finally + { + handle = SNINativeMethodWrapper.SNIServerEnumOpen(); + SqlClientEventSource.Log.TryTraceEvent(" {2} returned handle = {3}.", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumOpen), handle); + } + + if (handle != ADP.s_ptrZero) + { + while (more && !TdsParserStaticMethods.TimeoutHasExpired(s_timeoutTime)) + { + readLength = SNINativeMethodWrapper.SNIServerEnumRead(handle, buffer, bufferSize, out more); + + SqlClientEventSource.Log.TryTraceEvent(" {2} returned 'readlength':{3}, and 'more':{4} with 'bufferSize' of {5}", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumRead), + readLength, more, bufferSize); + if (readLength > bufferSize) + { + failure = true; + more = false; + } + else if (readLength > 0) + { + strbldr.Append(buffer, 0, readLength); + } + } + } + } + finally + { + if (handle != ADP.s_ptrZero) + { + SNINativeMethodWrapper.SNIServerEnumClose(handle); + SqlClientEventSource.Log.TryTraceEvent(" {2} called.", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumClose)); + } + } + + if (failure) + { + Debug.Assert(false, $"{nameof(GetDataSources)}:{nameof(SNINativeMethodWrapper.SNIServerEnumRead)} returned bad length"); + SqlClientEventSource.Log.TryTraceEvent(" {2} returned bad length, requested buffer {3}, received {4}", + nameof(SqlDataSourceEnumeratorNativeHelper), + nameof(GetDataSources), + nameof(SNINativeMethodWrapper.SNIServerEnumRead), + bufferSize, readLength); + + throw ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.ADP_ParameterValueOutOfRange, readLength), nameof(readLength)); + } + return ParseServerEnumString(strbldr.ToString()); + } + + private static DataTable ParseServerEnumString(string serverInstances) + { + DataTable dataTable = PrepareDataTable(); + string serverName = null; + string instanceName = null; + string isClustered = null; + string version = null; + string[] serverinstanceslist = serverInstances.Split(EndOfServerInstanceDelimiter_Native); + SqlClientEventSource.Log.TryTraceEvent(" Number of recieved server instances are {2}", + nameof(SqlDataSourceEnumeratorNativeHelper), nameof(ParseServerEnumString), serverinstanceslist.Length); + + // Every row comes in the format "serverName\instanceName;Clustered:[Yes|No];Version:.." + // Every row is terminated by a null character. + // Process one row at a time + foreach (string instance in serverinstanceslist) + { + string value = instance.Trim(EndOfServerInstanceDelimiter_Native); // MDAC 91934 + if (value.Length == 0) + { + continue; + } + foreach (string instance2 in value.Split(InstanceKeysDelimiter)) + { + if (serverName == null) + { + foreach (string instance3 in instance2.Split(ServerNamesAndInstanceDelimiter)) + { + if (serverName == null) + { + serverName = instance3; + continue; + } + Debug.Assert(instanceName == null, $"{nameof(instanceName)}({instanceName}) is not null."); + instanceName = instance3; + } + continue; + } + if (isClustered == null) + { + Debug.Assert(string.Compare(Clustered, 0, instance2, 0, s_clusteredLength, StringComparison.OrdinalIgnoreCase) == 0, + $"{nameof(Clustered)} ({Clustered}) doesn't equal {nameof(instance2)} ({instance2})"); + isClustered = instance2.Substring(s_clusteredLength); + continue; + } + Debug.Assert(version == null, $"{nameof(version)}({version}) is not null."); + Debug.Assert(string.Compare(SqlDataSourceEnumeratorUtil.Version, 0, instance2, 0, s_versionLength, StringComparison.OrdinalIgnoreCase) == 0, + $"{nameof(SqlDataSourceEnumeratorUtil.Version)} ({SqlDataSourceEnumeratorUtil.Version}) doesn't equal {nameof(instance2)} ({instance2})"); + version = instance2.Substring(s_versionLength); + } + + string query = "ServerName='" + serverName + "'"; + + if (!ADP.IsEmpty(instanceName)) + { // SQL BU DT 20006584: only append instanceName if present. + query += " AND InstanceName='" + instanceName + "'"; + } + + // SNI returns dupes - do not add them. SQL BU DT 290323 + if (dataTable.Select(query).Length == 0) + { + DataRow dataRow = dataTable.NewRow(); + dataRow[0] = serverName; + dataRow[1] = instanceName; + dataRow[2] = isClustered; + dataRow[3] = version; + dataTable.Rows.Add(dataRow); + } + serverName = null; + instanceName = null; + isClustered = null; + version = null; + } + return dataTable.SetColumnsReadOnly(); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs new file mode 100644 index 0000000000..fb6972d8cf --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Sql/SqlDataSourceEnumeratorUtil.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Data; +using System.Globalization; + +namespace Microsoft.Data.Sql +{ + /// + /// const values for SqlDataSourceEnumerator + /// + internal static class SqlDataSourceEnumeratorUtil + { + internal const string ServerNameCol = "ServerName"; + internal const string InstanceNameCol = "InstanceName"; + internal const string IsClusteredCol = "IsClustered"; + internal const string VersionNameCol = "Version"; + + internal const string Version = "Version:"; + internal const string Clustered = "Clustered:"; + internal static readonly int s_versionLength = Version.Length; + internal static readonly int s_clusteredLength = Clustered.Length; + + internal static readonly string[] s_endOfServerInstanceDelimiter_Managed = new[] { ";;" }; + internal const char EndOfServerInstanceDelimiter_Native = '\0'; + internal const char InstanceKeysDelimiter = ';'; + internal const char ServerNamesAndInstanceDelimiter = '\\'; + + internal static DataTable PrepareDataTable() + { + DataTable dataTable = new("SqlDataSources"); + dataTable.Locale = CultureInfo.InvariantCulture; + dataTable.Columns.Add(ServerNameCol, typeof(string)); + dataTable.Columns.Add(InstanceNameCol, typeof(string)); + dataTable.Columns.Add(IsClusteredCol, typeof(string)); + dataTable.Columns.Add(VersionNameCol, typeof(string)); + + return dataTable; + } + + /// + /// Sets all columns read-only. + /// + internal static DataTable SetColumnsReadOnly(this DataTable dataTable) + { + foreach (DataColumn column in dataTable.Columns) + { + column.ReadOnly = true; + } + return dataTable; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs deleted file mode 100644 index cc652a2e1a..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AlwaysEncryptedAttestationException.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Data.SqlClient -{ - internal class AlwaysEncryptedAttestationException : Exception - { - public AlwaysEncryptedAttestationException(string message, Exception innerException) : base(message, innerException) { } - - public AlwaysEncryptedAttestationException(string message) : base(message) { } - - public AlwaysEncryptedAttestationException() : base() { } - } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs index 0e891924fb..433347ef10 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/AzureAttestationBasedEnclaveProvider.cs @@ -109,7 +109,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell } else { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } } @@ -213,7 +213,7 @@ public AzureAttestationInfo(byte[] attestationInfo) } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.FailToParseAttestationInfo, exception.Message)); + throw SQL.AttestationFailed(string.Format(Strings.FailToParseAttestationInfo, exception.Message)); } } } @@ -275,7 +275,7 @@ internal byte[] PrepareAttestationParameters(string attestationUrl, byte[] attes } else { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } @@ -311,7 +311,7 @@ private void VerifyAzureAttestationInfo(string attestationUrl, EnclaveType encla if (!isSignatureValid) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); + throw SQL.AttestationFailed(string.Format(Strings.AttestationTokenSignatureValidationFailed, exceptionMessage)); } // Validate claims in the token @@ -347,7 +347,7 @@ private OpenIdConnectConfiguration GetOpenIdConfigForSigningKeys(string url, boo } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); + throw SQL.AttestationFailed(string.Format(Strings.GetAttestationTokenSigningKeysFailed, GetInnerMostExceptionMessage(exception)), exception); } OpenIdConnectConfigurationCache.Add(url, openIdConnectConfig, DateTime.UtcNow.AddDays(1)); @@ -414,7 +414,7 @@ private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl } catch (SecurityTokenExpiredException securityException) { - throw new AlwaysEncryptedAttestationException(Strings.ExpiredAttestationToken, securityException); + throw SQL.AttestationFailed(Strings.ExpiredAttestationToken, securityException); } catch (SecurityTokenValidationException securityTokenException) { @@ -426,7 +426,7 @@ private bool VerifyTokenSignature(string attestationToken, string tokenIssuerUrl } catch (Exception exception) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); + throw SQL.AttestationFailed(string.Format(Strings.InvalidAttestationToken, GetInnerMostExceptionMessage(exception))); } return isSignatureValid; @@ -445,7 +445,7 @@ private byte[] ComputeSHA256(byte[] data) } catch (Exception argumentException) { - throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToSHA256, argumentException); + throw SQL.AttestationFailed(Strings.InvalidArgumentToSHA256, argumentException); } return result; } @@ -462,7 +462,7 @@ private void ValidateAttestationClaims(EnclaveType enclaveType, string attestati } catch (ArgumentException argumentException) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.FailToParseAttestationToken, argumentException.Message)); + throw SQL.AttestationFailed(string.Format(Strings.FailToParseAttestationToken, argumentException.Message)); } // Get all the claims from the token @@ -490,7 +490,7 @@ private void ValidateClaim(Dictionary claims, string claimName, bool hasClaim = claims.TryGetValue(claimName, out claimData); if (!hasClaim) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.MissingClaimInAttestationToken, claimName)); + throw SQL.AttestationFailed(string.Format(Strings.MissingClaimInAttestationToken, claimName)); } // Get the Base64Url of the actual data and compare it with claim @@ -501,13 +501,13 @@ private void ValidateClaim(Dictionary claims, string claimName, } catch (Exception) { - throw new AlwaysEncryptedAttestationException(Strings.InvalidArgumentToBase64UrlDecoder); + throw SQL.AttestationFailed(Strings.InvalidArgumentToBase64UrlDecoder); } bool hasValidClaim = string.Equals(encodedActualData, claimData, StringComparison.Ordinal); if (!hasValidClaim) { - throw new AlwaysEncryptedAttestationException(string.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); + throw SQL.AttestationFailed(string.Format(Strings.InvalidClaimInAttestationToken, claimName, claimData)); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs index fbc2b50523..d1e6c71527 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.Crypto.cs @@ -10,7 +10,7 @@ namespace Microsoft.Data.SqlClient { internal sealed partial class EnclaveDelegate { - private static readonly Dictionary s_enclaveProviders = new Dictionary(); + private static readonly ConcurrentDictionary s_enclaveProviders = new(); /// /// Create a new enclave session @@ -101,13 +101,11 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: + case SqlConnectionAttestationProtocol.None: NoneAttestationEnclaveProvider noneAttestationEnclaveProvider = new NoneAttestationEnclaveProvider(); - s_enclaveProviders[attestationProtocol] = (SqlColumnEncryptionEnclaveProvider)noneAttestationEnclaveProvider; + s_enclaveProviders[attestationProtocol] = noneAttestationEnclaveProvider; sqlColumnEncryptionEnclaveProvider = s_enclaveProviders[attestationProtocol]; break; -#endif default: break; @@ -116,7 +114,7 @@ private SqlColumnEncryptionEnclaveProvider GetEnclaveProvider(SqlConnectionAttes if (sqlColumnEncryptionEnclaveProvider == null) { - throw SQL.EnclaveProviderNotFound(enclaveType, ConvertAttestationProtocolToString(attestationProtocol)); + throw SQL.EnclaveProviderNotFound(enclaveType, attestationProtocol.ToString()); } return sqlColumnEncryptionEnclaveProvider; @@ -208,25 +206,5 @@ internal byte[] GetSerializedAttestationParameters(SqlEnclaveAttestationParamete return CombineByteArrays(attestationProtocolBytes, attestationProtocolInputLengthBytes, attestationProtocolInputBytes, clientDHPublicKeyLengthBytes, clientDHPublicKey); } - - private string ConvertAttestationProtocolToString(SqlConnectionAttestationProtocol attestationProtocol) - { - switch (attestationProtocol) - { - case SqlConnectionAttestationProtocol.AAS: - return "AAS"; - - case SqlConnectionAttestationProtocol.HGS: - return "HGS"; - -#if ENCLAVE_SIMULATOR - case SqlConnectionAttestationProtocol.SIM: - return "SIM"; -#endif - - default: - return "NotSpecified"; - } - } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs index ff36d1604c..84aea73940 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/NoneAttestationEnclaveProvider.cs @@ -13,7 +13,8 @@ internal class NoneAttestationEnclaveProvider : EnclaveProviderBase { private static readonly int EnclaveSessionHandleSize = 8; private const int DiffieHellmanKeySize = 384; - private const int NoneAttestationProtocolId = 2; + private const int NoneAttestationProtocolId = (int)SqlConnectionAttestationProtocol.None; + // When overridden in a derived class, looks up an existing enclave session information in the enclave session cache. // If the enclave provider doesn't implement enclave session caching, this method is expected to return null in the sqlEnclaveSession parameter. @@ -44,47 +45,45 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell if (sqlEnclaveSession == null) { - if (!string.IsNullOrEmpty(enclaveSessionParameters.AttestationUrl)) - { - // Read AttestationInfo - int attestationInfoOffset = 0; - uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); - Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); - - // read secure session info - uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - - byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); - attestationInfoOffset += EnclaveSessionHandleSize; - - uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); - attestationInfoOffset += sizeof(UInt32); - int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); - - byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, - sizeOfTrustedModuleDHPublicKeyBufferInt); - attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; - - byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; - Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, - checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); - - byte[] sharedSecret; - using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); - sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); - long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); - sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); - } - else + // Read AttestationInfo + int attestationInfoOffset = 0; + uint sizeOfTrustedModuleAttestationInfoBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleAttestationInfoBufferInt = checked((int)sizeOfTrustedModuleAttestationInfoBuffer); + Debug.Assert(sizeOfTrustedModuleAttestationInfoBuffer == 0); + + // read secure session info + uint sizeOfSecureSessionInfoResponse = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + + byte[] enclaveSessionHandle = new byte[EnclaveSessionHandleSize]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, enclaveSessionHandle, 0, EnclaveSessionHandleSize); + attestationInfoOffset += EnclaveSessionHandleSize; + + uint sizeOfTrustedModuleDHPublicKeyBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + uint sizeOfTrustedModuleDHPublicKeySignatureBuffer = BitConverter.ToUInt32(attestationInfo, attestationInfoOffset); + attestationInfoOffset += sizeof(UInt32); + int sizeOfTrustedModuleDHPublicKeyBufferInt = checked((int)sizeOfTrustedModuleDHPublicKeyBuffer); + + byte[] trustedModuleDHPublicKey = new byte[sizeOfTrustedModuleDHPublicKeyBuffer]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKey, 0, + sizeOfTrustedModuleDHPublicKeyBufferInt); + attestationInfoOffset += sizeOfTrustedModuleDHPublicKeyBufferInt; + + byte[] trustedModuleDHPublicKeySignature = new byte[sizeOfTrustedModuleDHPublicKeySignatureBuffer]; + Buffer.BlockCopy(attestationInfo, attestationInfoOffset, trustedModuleDHPublicKeySignature, 0, + checked((int)sizeOfTrustedModuleDHPublicKeySignatureBuffer)); + + byte[] sharedSecret; + using ECDiffieHellman ecdh = KeyConverter.CreateECDiffieHellmanFromPublicKeyBlob(trustedModuleDHPublicKey); + sharedSecret = KeyConverter.DeriveKey(clientDHKey, ecdh.PublicKey); + long sessionId = BitConverter.ToInt64(enclaveSessionHandle, 0); + sqlEnclaveSession = AddEnclaveSessionToCache(enclaveSessionParameters, sharedSecret, sessionId, out counter); + + if (sqlEnclaveSession is null) { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/IBinarySerialize.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/IBinarySerialize.cs deleted file mode 100644 index 9b08f58d39..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/IBinarySerialize.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; - -namespace Microsoft.Data.SqlClient.Server -{ - /// - // This interface is used by types that want full control over the - // binary serialization format. - public interface IBinarySerialize - { - /// - // Read from the specified binary reader. - void Read(BinaryReader r); - /// - // Write to the specified binary writer. - void Write(BinaryWriter w); - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs index 85abc33550..590429222a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs @@ -13,21 +13,24 @@ namespace Microsoft.Data.SqlClient.Server { - // Utilities for manipulating smi-related metadata. - // - // Since this class is built on top of SMI, SMI should not have a dependency on this class - // - // These are all based off of knowing the clr type of the value - // as an ExtendedClrTypeCode enum for rapid access. + /// + /// Utilities for manipulating smi-related metadata. + /// Since this class is built on top of SMI, SMI should not have a dependency on this class + /// These are all based off of knowing the clr type of the value + /// as an ExtendedClrTypeCode enum for rapid access. + /// internal class MetaDataUtilsSmi { internal const SqlDbType InvalidSqlDbType = (SqlDbType)(-1); internal const long InvalidMaxLength = -2; - // Standard type inference map to get SqlDbType when all you know is the value's type (typecode) - // This map's index is off by one (add one to typecode locate correct entry) in order - // to support ExtendedSqlDbType.Invalid - // This array is meant to be accessed from InferSqlDbTypeFromTypeCode. + + /// + /// Standard type inference map to get SqlDbType when all you know is the value's type (typecode) + /// This map's index is off by one (add one to typecode locate correct entry) in order + /// to support ExtendedSqlDbType.Invalid + /// This array is meant to be accessed from InferSqlDbTypeFromTypeCode. + /// private static readonly SqlDbType[] s_extendedTypeCodeToSqlDbTypeMap = { InvalidSqlDbType, // Invalid extended type code SqlDbType.Bit, // System.Boolean @@ -74,8 +77,11 @@ internal class MetaDataUtilsSmi SqlDbType.DateTimeOffset, // System.DateTimeOffset }; - // Dictionary to map from clr type object to ExtendedClrTypeCodeMap enum. - // This dictionary should only be accessed from DetermineExtendedTypeCode and class ctor for setup. + + /// + /// Dictionary to map from clr type object to ExtendedClrTypeCodeMap enum. + /// This dictionary should only be accessed from DetermineExtendedTypeCode and class ctor for setup. + /// private static readonly Dictionary s_typeToExtendedTypeCodeMap = CreateTypeToExtendedTypeCodeMap(); private static Dictionary CreateTypeToExtendedTypeCodeMap() @@ -130,12 +136,9 @@ private static Dictionary CreateTypeToExtendedTypeCod return dictionary; } - internal static bool IsCharOrXmlType(SqlDbType type) - { - return IsUnicodeType(type) || + internal static bool IsCharOrXmlType(SqlDbType type) => IsUnicodeType(type) || IsAnsiType(type) || type == SqlDbType.Xml; - } internal static bool IsUnicodeType(SqlDbType type) { @@ -144,29 +147,21 @@ internal static bool IsUnicodeType(SqlDbType type) type == SqlDbType.NText; } - internal static bool IsAnsiType(SqlDbType type) - { - return type == SqlDbType.Char || + internal static bool IsAnsiType(SqlDbType type) => type == SqlDbType.Char || type == SqlDbType.VarChar || type == SqlDbType.Text; - } - internal static bool IsBinaryType(SqlDbType type) - { - return type == SqlDbType.Binary || + internal static bool IsBinaryType(SqlDbType type) => type == SqlDbType.Binary || type == SqlDbType.VarBinary || type == SqlDbType.Image; - } // Does this type use PLP format values? - internal static bool IsPlpFormat(SmiMetaData metaData) - { - return metaData.MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator || + internal static bool IsPlpFormat(SmiMetaData metaData) => + metaData.MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator || metaData.SqlDbType == SqlDbType.Image || metaData.SqlDbType == SqlDbType.NText || metaData.SqlDbType == SqlDbType.Text || metaData.SqlDbType == SqlDbType.Udt; - } // If we know we're only going to use this object to assign to a specific SqlDbType back end object, // we can save some processing time by only checking for the few valid types that can be assigned to the dbType. @@ -183,7 +178,11 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType SqlDbType dbType, bool isMultiValued, object value, - Type udtType) + Type udtType +#if NETFRAMEWORK + ,ulong smiVersion +#endif + ) { ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid; @@ -246,6 +245,13 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType break; case SqlDbType.Date: case SqlDbType.DateTime2: +#if NETFRAMEWORK + if (smiVersion >= SmiContextFactory.Sql2008Version) + { + goto case SqlDbType.DateTime; + } + break; +#endif case SqlDbType.DateTime: case SqlDbType.SmallDateTime: if (value.GetType() == typeof(DateTime)) @@ -325,11 +331,19 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType } break; case SqlDbType.Time: - if (value.GetType() == typeof(TimeSpan)) + if (value.GetType() == typeof(TimeSpan) +#if NETFRAMEWORK + && smiVersion >= SmiContextFactory.Sql2008Version +#endif + ) extendedCode = ExtendedClrTypeCode.TimeSpan; break; case SqlDbType.DateTimeOffset: - if (value.GetType() == typeof(DateTimeOffset)) + if (value.GetType() == typeof(DateTimeOffset) +#if NETFRAMEWORK + && smiVersion >= SmiContextFactory.Sql2008Version +#endif + ) extendedCode = ExtendedClrTypeCode.DateTimeOffset; break; case SqlDbType.Xml: @@ -424,35 +438,11 @@ internal static SqlDbType InferSqlDbTypeFromType_2008(Type type) return returnType; } - internal static SqlMetaData SmiExtendedMetaDataToSqlMetaData(SmiExtendedMetaData source) - { - if (SqlDbType.Xml == source.SqlDbType) - { - return new SqlMetaData(source.Name, - source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - source.TypeSpecificNamePart1, - source.TypeSpecificNamePart2, - source.TypeSpecificNamePart3, - true, - source.Type); - } - - return new SqlMetaData(source.Name, - source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - null); - } - - // Convert SqlMetaData instance to an SmiExtendedMetaData instance. + /// + /// Convert SqlMetaData instance to an SmiExtendedMetaData instance. + /// + /// + /// internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData(SqlMetaData source) { // now map everything across to the extended metadata object @@ -510,7 +500,11 @@ internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData(SqlMetaData source.Scale, source.LocaleId, source.CompareOptions, +#if NETFRAMEWORK + source.Type, +#else null, +#endif source.Name, typeSpecificNamePart1, typeSpecificNamePart2, @@ -526,6 +520,7 @@ internal static bool IsCompatible(SmiMetaData firstMd, SqlMetaData secondMd) firstMd.Scale == secondMd.Scale && firstMd.CompareOptions == secondMd.CompareOptions && firstMd.LocaleId == secondMd.LocaleId && + firstMd.Type == secondMd.Type && firstMd.SqlDbType != SqlDbType.Structured && // SqlMetaData doesn't support Structured types !firstMd.IsMultiValued; // SqlMetaData doesn't have a "multivalued" option } @@ -645,7 +640,11 @@ internal static SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, scale, columnLocale.LCID, SmiMetaData.DefaultNVarChar.CompareOptions, +#if NETFRAMEWORK + column.DataType, +#else null, +#endif false, // no support for multi-valued columns in a TVP yet null, // no support for structured columns yet null, // no support for structured columns yet @@ -959,5 +958,27 @@ internal static SmiExtendedMetaData SmiMetaDataFromSchemaTableRow(DataRow schema null, null); } + +#if NETFRAMEWORK + + static internal bool IsValidForSmiVersion(SmiExtendedMetaData md, ulong smiVersion) + { + if (SmiContextFactory.LatestVersion == smiVersion) + { + return true; + } + else + { + // 2005 doesn't support Structured nor the new time types + Debug.Assert(SmiContextFactory.Sql2005Version == smiVersion, "Other versions should have been eliminated during link stage"); + return md.SqlDbType != SqlDbType.Structured && + md.SqlDbType != SqlDbType.Date && + md.SqlDbType != SqlDbType.DateTime2 && + md.SqlDbType != SqlDbType.DateTimeOffset && + md.SqlDbType != SqlDbType.Time; + } + } + +#endif } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs index 9f5c11f60c..314807d52e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs @@ -296,7 +296,7 @@ public virtual int GetSqlValues(object[] values) public virtual void SetDBNull(int ordinal) { ThrowIfInvalidOrdinal(ordinal); - ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal, true); + ValueUtilsSmi.SetDBNull(_eventSink, _recordBuffer, ordinal); } /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netcore.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netcore.cs index 106d9f6b0a..4c25f693d5 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netcore.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netcore.cs @@ -16,18 +16,18 @@ private Type GetFieldTypeFrameworkSpecific(int ordinal) => MetaType.GetMetaTypeFromSqlDbType(GetSqlMetaData(ordinal).SqlDbType, false).ClassType; private object GetValueFrameworkSpecific(int ordinal) - => ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + => ValueUtilsSmi.GetValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), null); private object GetSqlValueFrameworkSpecific(int ordinal) - => ValueUtilsSmi.GetSqlValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + => ValueUtilsSmi.GetSqlValue200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), null); private SqlBytes GetSqlBytesFrameworkSpecific(int ordinal) - => ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + => ValueUtilsSmi.GetSqlBytes(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), null); private SqlXml GetSqlXmlFrameworkSpecific(int ordinal) - => ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + => ValueUtilsSmi.GetSqlXml(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), null); private SqlChars GetSqlCharsFrameworkSpecific(int ordinal) - => ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal)); + => ValueUtilsSmi.GetSqlChars(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), null); private int SetValuesFrameworkSpecific(params object[] values) { if (values == null) @@ -60,7 +60,7 @@ private int SetValuesFrameworkSpecific(params object[] values) // the validation loop and here, or if an invalid UDT was sent). for (int i = 0; i < copyLength; i++) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, length: 0, peekAhead: null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, peekAhead: null); } return copyLength; @@ -80,7 +80,7 @@ private void SetValueFrameworkSpecific(int ordinal, object value) throw ADP.InvalidCast(); } - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, length: 0, peekAhead: null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, peekAhead: null); } private void SetTimeSpanFrameworkSpecific(int ordinal, TimeSpan value) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netfx.cs index 29e2bf9305..45d4be4d6b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.netfx.cs @@ -91,7 +91,7 @@ private int SetValuesFrameworkSpecific(params object[] values) { if (SmiVersion >= SmiContextFactory.Sql2008Version) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, length: 0, peekAhead: null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, i, GetSmiMetaData(i), values[i], typeCodes[i], offset: 0, peekAhead: null); } else { @@ -119,7 +119,7 @@ private void SetValueFrameworkSpecific(int ordinal, object value) if (SmiVersion >= SmiContextFactory.Sql2008Version) { - ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, length: 0, peekAhead: null); + ValueUtilsSmi.SetCompatibleValueV200(_eventSink, _recordBuffer, ordinal, GetSmiMetaData(ordinal), value, typeCode, offset: 0, peekAhead: null); } else { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFacetAttribute.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFacetAttribute.cs deleted file mode 100644 index da80e953b8..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFacetAttribute.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Data.SqlClient.Server -{ - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] - public class SqlFacetAttribute : Attribute - { - /// - public SqlFacetAttribute() { } - - /// - public bool IsFixedLength - { - get; - set; - } - - /// - public int MaxSize - { - get; - set; - } - - /// - public int Precision - { - get; - set; - } - - /// - public int Scale - { - get; - set; - } - - /// - public bool IsNullable - { - get; - set; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFunctionAttribute.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFunctionAttribute.cs deleted file mode 100644 index 87b84c7ba1..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlFunctionAttribute.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Data.SqlClient.Server -{ - /// - [Serializable] - public enum DataAccessKind - { - /// - None = 0, - /// - Read = 1, - } - - /// - [Serializable] - public enum SystemDataAccessKind - { - /// - None = 0, - /// - Read = 1, - } - - /// - // sql specific attribute - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false), Serializable] - public class SqlFunctionAttribute : Attribute - { - private bool _isDeterministic; - private DataAccessKind _dataAccess; - private SystemDataAccessKind _systemDataAccess; - private bool _isPrecise; - private string _name; - private string _tableDefinition; - private string _fillRowMethodName; - - /// - public SqlFunctionAttribute() - { - // default values - _isDeterministic = false; - _dataAccess = DataAccessKind.None; - _systemDataAccess = SystemDataAccessKind.None; - _isPrecise = false; - _name = null; - _tableDefinition = null; - _fillRowMethodName = null; - } - - /// - public bool IsDeterministic - { - get => _isDeterministic; - set => _isDeterministic = value; - } - - /// - public DataAccessKind DataAccess - { - get => _dataAccess; - set => _dataAccess = value; - } - - /// - public SystemDataAccessKind SystemDataAccess - { - get => _systemDataAccess; - set => _systemDataAccess = value; - } - - /// - public bool IsPrecise - { - get => _isPrecise; - set => _isPrecise = value; - } - - /// - public string Name - { - get => _name; - set => _name = value; - } - - /// - public string TableDefinition - { - get => _tableDefinition; - set => _tableDefinition = value; - } - - /// - public string FillRowMethodName - { - get => _fillRowMethodName; - set => _fillRowMethodName = value; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs index e5bb5b0047..a8d372aa2e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMetaData.cs @@ -1285,7 +1285,7 @@ public SqlBinary Adjust(SqlBinary value) { byte[] rgbValue = value.Value; byte[] rgbNewValue = new byte[MaxLength]; - Array.Copy(rgbValue, rgbNewValue, rgbValue.Length); + Buffer.BlockCopy(rgbValue, 0, rgbNewValue, 0, rgbValue.Length); Array.Clear(rgbNewValue, rgbValue.Length, rgbNewValue.Length - rgbValue.Length); return new SqlBinary(rgbNewValue); } @@ -1307,7 +1307,7 @@ public SqlBinary Adjust(SqlBinary value) { byte[] rgbValue = value.Value; byte[] rgbNewValue = new byte[MaxLength]; - Array.Copy(rgbValue, rgbNewValue, (int)MaxLength); + Buffer.BlockCopy(rgbValue,0, rgbNewValue,0, (int)MaxLength); value = new SqlBinary(rgbNewValue); } @@ -1399,7 +1399,7 @@ public SqlBytes Adjust(SqlBytes value) if (value.MaxLength < MaxLength) { byte[] rgbNew = new byte[MaxLength]; - Array.Copy(value.Buffer, rgbNew, (int)oldLength); + Buffer.BlockCopy(value.Buffer, 0,rgbNew,0, (int)oldLength); value = new SqlBytes(rgbNew); } @@ -1928,7 +1928,7 @@ public byte[] Adjust(byte[] value) if (value.Length < MaxLength) { byte[] rgbNewValue = new byte[MaxLength]; - Array.Copy(value, rgbNewValue, value.Length); + Buffer.BlockCopy(value, 0, rgbNewValue, 0, value.Length); Array.Clear(rgbNewValue, value.Length, (int)rgbNewValue.Length - value.Length); return rgbNewValue; } @@ -1949,7 +1949,7 @@ public byte[] Adjust(byte[] value) if (value.Length > MaxLength && Max != MaxLength) { byte[] rgbNewValue = new byte[MaxLength]; - Array.Copy(value, rgbNewValue, (int)MaxLength); + Buffer.BlockCopy(value, 0, rgbNewValue, 0, (int)MaxLength); value = rgbNewValue; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlSer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlSer.cs similarity index 67% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlSer.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlSer.cs index c9f1536f50..1f9a4181f0 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/SqlSer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlSer.cs @@ -3,15 +3,15 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; +using System.Collections.Concurrent; using System.IO; -using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.Data.Common; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient.Server { - internal class SerializationHelperSql9 + internal sealed class SerializationHelperSql9 { // Don't let anyone create an instance of this class. private SerializationHelperSql9() { } @@ -28,7 +28,8 @@ private SerializationHelperSql9() { } internal static int SizeInBytes(object instance) { Type t = instance.GetType(); - Format k = GetFormat(t); + + _ = GetFormat(t); DummyStream stream = new DummyStream(); Serializer ser = GetSerializer(instance.GetType()); ser.Serialize(stream, instance); @@ -50,20 +51,22 @@ internal static void Serialize(Stream s, object instance) // // Use a per-thread cache, so that there are no synchronization // issues when accessing cache entries from multiple threads. - [ThreadStatic] - private static Hashtable s_types2Serializers; + private static ConcurrentDictionary s_types2Serializers; private static Serializer GetSerializer(Type t) { if (s_types2Serializers == null) - s_types2Serializers = new Hashtable(); + { + s_types2Serializers = new ConcurrentDictionary(); + } - Serializer s = (Serializer)s_types2Serializers[t]; - if (s == null) + Serializer s; + if (!s_types2Serializers.TryGetValue(t, out s)) { s = GetNewSerializer(t); s_types2Serializers[t] = s; } + return s; } @@ -85,68 +88,19 @@ internal static int GetUdtMaxLength(Type t) } private static object[] GetCustomAttributes(Type t) - { - object[] attrs = t.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); - - // If we don't find a Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute, - // search for a Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute from the - // old System.Data.SqlClient assembly and copy it to our - // Microsoft.Data.SqlClient.Server.SqlUserDefinedTypeAttribute for reference. - if (attrs == null || attrs.Length == 0) - { - object[] attr = t.GetCustomAttributes(false); - attrs = new object[0]; - if (attr != null && attr.Length > 0) - { - for (int i = 0; i < attr.Length; i++) - { - if (attr[i].GetType().FullName.Equals("Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute")) - { - SqlUserDefinedTypeAttribute newAttr = null; - PropertyInfo[] sourceProps = attr[i].GetType().GetProperties(); - - foreach (PropertyInfo sourceProp in sourceProps) - { - if (sourceProp.Name.Equals("Format")) - { - newAttr = new SqlUserDefinedTypeAttribute((Format)sourceProp.GetValue(attr[i], null)); - break; - } - } - if (newAttr != null) - { - foreach (PropertyInfo targetProp in newAttr.GetType().GetProperties()) - { - if (targetProp.CanRead && targetProp.CanWrite) - { - object copyValue = attr[i].GetType().GetProperty(targetProp.Name).GetValue(attr[i]); - targetProp.SetValue(newAttr, copyValue); - } - } - } - - attrs = new object[1] { newAttr }; - break; - } - } - } - } - - return attrs; - } + => t.GetCustomAttributes(typeof(SqlUserDefinedTypeAttribute), false); internal static SqlUserDefinedTypeAttribute GetUdtAttribute(Type t) { - SqlUserDefinedTypeAttribute udtAttr = null; + SqlUserDefinedTypeAttribute udtAttr; object[] attr = GetCustomAttributes(t); - if (attr != null && attr.Length == 1) { udtAttr = (SqlUserDefinedTypeAttribute)attr[0]; } else { - throw InvalidUdtException.Create(t, Strings.SqlUdtReason_NoUdtAttribute); + throw ADP.CreateInvalidUdtException(t, nameof(Strings.SqlUdtReason_NoUdtAttribute)); } return udtAttr; } @@ -155,9 +109,8 @@ internal static SqlUserDefinedTypeAttribute GetUdtAttribute(Type t) private static Serializer GetNewSerializer(Type t) { SqlUserDefinedTypeAttribute udtAttr = GetUdtAttribute(t); - Format k = GetFormat(t); - - switch (k) + + switch (udtAttr.Format) { case Format.Native: return new NormalizedSerializer(t); @@ -165,7 +118,7 @@ private static Serializer GetNewSerializer(Type t) return new BinarySerializeSerializer(t); case Format.Unknown: // should never happen, but fall through default: - throw ADP.InvalidUserDefinedTypeSerializationFormat(k); + throw ADP.InvalidUserDefinedTypeSerializationFormat(udtAttr.Format); } } } @@ -183,16 +136,12 @@ internal abstract class Serializer internal sealed class NormalizedSerializer : Serializer { - private BinaryOrderedUdtNormalizer _normalizer; - private bool _isFixedSize; - private int _maxSize; - + private readonly BinaryOrderedUdtNormalizer _normalizer; + internal NormalizedSerializer(Type t) : base(t) { - SqlUserDefinedTypeAttribute udtAttr = SerializationHelperSql9.GetUdtAttribute(t); + _ = SerializationHelperSql9.GetUdtAttribute(t); _normalizer = new BinaryOrderedUdtNormalizer(t, true); - _isFixedSize = udtAttr.IsFixedLength; - _maxSize = _normalizer.Size; } public override void Serialize(Stream s, object o) => _normalizer.NormalizeTopObject(o, s); @@ -220,7 +169,7 @@ public override object Deserialize(Stream s) { object instance = Activator.CreateInstance(_type); BinaryReader r = new BinaryReader(s); - ((IBinarySerialize)instance).Read(r); + ((IBinarySerialize)instance).Read(r); return instance; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs index 553377e005..0548bbf126 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs @@ -15,6 +15,10 @@ using Microsoft.Data.Common; using Microsoft.Data.SqlTypes; +#if !NETFRAMEWORK +using SmiContext = System.Object; +#endif + namespace Microsoft.Data.SqlClient.Server { // Utilities for manipulating values with the Smi interface. @@ -25,13 +29,13 @@ namespace Microsoft.Data.SqlClient.Server // as an ExtendedClrTypeCode enum for rapid access (lookup in static array is best, if possible). internal static partial class ValueUtilsSmi { - private const int __maxByteChunkSize = TdsEnums.MAXSIZE; - private const int __maxCharChunkSize = TdsEnums.MAXSIZE / sizeof(char); + private const int MaxByteChunkSize = TdsEnums.MAXSIZE; + private const int MaxCharChunkSize = TdsEnums.MAXSIZE / sizeof(char); private const int NoLengthLimit = (int)SmiMetaData.UnlimitedMaxLengthIndicator; // make sure we use the same constant // Constants - private const int constBinBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream - private const int constTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader + private const int DefaultBinaryBufferSize = 4096; // Size of the buffer used to read input parameter of type Stream + private const int DefaultTextBufferSize = 4096; // Size of the buffer (in chars) user to read input parameter of type TextReader // // User-visible semantics-laden Getter/Setter support methods @@ -60,12 +64,8 @@ internal static bool GetBoolean(SmiEventSink_Default sink, ITypedGettersV3 gette return GetBoolean_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -79,12 +79,8 @@ internal static byte GetByte(SmiEventSink_Default sink, ITypedGettersV3 getters, { return GetByte_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -93,12 +89,8 @@ internal static byte GetByte(SmiEventSink_Default sink, ITypedGettersV3 getters, private static long GetBytesConversion(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData, long fieldOffset, byte[] buffer, int bufferOffset, int length, bool throwOnNull) { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj== null) { throw ADP.InvalidCast(); } @@ -117,26 +109,32 @@ private static long GetBytesConversion(SmiEventSink_Default sink, ITypedGettersV } } - if (null == buffer) + if (buffer == null) { return value.Length; } length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength * sizeof(char), value.Length, fieldOffset, buffer.Length, bufferOffset, length); - Array.Copy(value.Value, checked((int)fieldOffset), buffer, bufferOffset, length); + Buffer.BlockCopy(value.Value, checked((int)fieldOffset), buffer, bufferOffset, length); return length; } internal static long GetBytes(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiExtendedMetaData metaData, long fieldOffset, byte[] buffer, int bufferOffset, int length, bool throwOnNull) { // Additional exclusions not caught by GetBytesInternal - if ((SmiMetaData.UnlimitedMaxLengthIndicator != metaData.MaxLength && - (SqlDbType.VarChar == metaData.SqlDbType || - SqlDbType.NVarChar == metaData.SqlDbType || - SqlDbType.Char == metaData.SqlDbType || - SqlDbType.NChar == metaData.SqlDbType)) || - SqlDbType.Xml == metaData.SqlDbType) + if ( + ( + SmiMetaData.UnlimitedMaxLengthIndicator != metaData.MaxLength && + ( + metaData.SqlDbType == SqlDbType.VarChar || + metaData.SqlDbType == SqlDbType.NVarChar || + metaData.SqlDbType == SqlDbType.Char || + metaData.SqlDbType == SqlDbType.NChar + ) + ) || + SqlDbType.Xml == metaData.SqlDbType + ) { throw SQL.NonBlobColumn(metaData.Name); } @@ -166,14 +164,15 @@ internal static long GetBytesInternal(SmiEventSink_Default sink, ITypedGettersV3 } } long actualLength = GetBytesLength_Unchecked(sink, getters, ordinal); - if (null == buffer) + if (buffer == null) { return actualLength; } if (MetaDataUtilsSmi.IsCharOrXmlType(metaData.SqlDbType)) { - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength * sizeof(char), actualLength, - fieldOffset, buffer.Length, bufferOffset, length); + length = CheckXetParameters( + metaData.SqlDbType, metaData.MaxLength * sizeof(char), actualLength, fieldOffset, buffer.Length, bufferOffset, length + ); } else { @@ -196,7 +195,7 @@ internal static long GetChars(SmiEventSink_Default sink, ITypedGettersV3 getters if (CanAccessGetterDirectly(metaData, ExtendedClrTypeCode.CharArray)) { long actualLength = GetCharsLength_Unchecked(sink, getters, ordinal); - if (null == buffer) + if (buffer == null) { return actualLength; } @@ -209,21 +208,18 @@ internal static long GetChars(SmiEventSink_Default sink, ITypedGettersV3 getters return length; } - string value = ((string)GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - )); - if (null == value) + string value = (string)GetValue(sink, getters, ordinal, metaData); + if (value == null) { throw ADP.InvalidCast(); } - if (null == buffer) + if (buffer == null) { return value.Length; } - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength * sizeof(char), value.Length, - fieldOffset, buffer.Length, bufferOffset, length); + length = CheckXetParameters( + metaData.SqlDbType, metaData.MaxLength * sizeof(char), value.Length, fieldOffset, buffer.Length, bufferOffset, length + ); value.CopyTo(checked((int)fieldOffset), buffer, bufferOffset, length); return length; } @@ -235,12 +231,8 @@ internal static DateTime GetDateTime(SmiEventSink_Default sink, ITypedGettersV3 { return GetDateTime_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -255,12 +247,8 @@ internal static DateTimeOffset GetDateTimeOffset(SmiEventSink_Default sink, ITyp return GetDateTimeOffset(sink, (SmiTypedGetterSetter)getters, ordinal, metaData); } ThrowIfITypedGettersIsNull(sink, getters, ordinal); - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -275,11 +263,7 @@ internal static DateTimeOffset GetDateTimeOffset(SmiEventSink_Default sink, SmiT { return GetDateTimeOffset_Unchecked(sink, getters, ordinal); } - return (DateTimeOffset)GetValue200(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); + return (DateTimeOffset)GetValue200(sink, getters, ordinal, metaData, null); } internal static decimal GetDecimal(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData) @@ -289,12 +273,8 @@ internal static decimal GetDecimal(SmiEventSink_Default sink, ITypedGettersV3 ge { return GetDecimal_PossiblyMoney(sink, getters, ordinal, metaData); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -308,12 +288,8 @@ internal static double GetDouble(SmiEventSink_Default sink, ITypedGettersV3 gett { return GetDouble_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -327,12 +303,8 @@ internal static Guid GetGuid(SmiEventSink_Default sink, ITypedGettersV3 getters, { return GetGuid_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -346,12 +318,8 @@ internal static short GetInt16(SmiEventSink_Default sink, ITypedGettersV3 getter { return GetInt16_Unchecked(sink, getters, ordinal); } - object obj = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -365,12 +333,8 @@ internal static int GetInt32(SmiEventSink_Default sink, ITypedGettersV3 getters, { return GetInt32_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -384,12 +348,8 @@ internal static long GetInt64(SmiEventSink_Default sink, ITypedGettersV3 getters { return GetInt64_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -403,12 +363,8 @@ internal static float GetSingle(SmiEventSink_Default sink, ITypedGettersV3 gette { return GetSingle_Unchecked(sink, getters, ordinal); } - object result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -425,12 +381,8 @@ internal static SqlBinary GetSqlBinary(SmiEventSink_Default sink, ITypedGettersV } return GetSqlBinary_Unchecked(sink, getters, ordinal); } - object result = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetSqlValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -447,12 +399,8 @@ internal static SqlBoolean GetSqlBoolean(SmiEventSink_Default sink, ITypedGetter } return new SqlBoolean(GetBoolean_Unchecked(sink, getters, ordinal)); } - object result = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetSqlValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } @@ -469,23 +417,15 @@ internal static SqlByte GetSqlByte(SmiEventSink_Default sink, ITypedGettersV3 ge } return new SqlByte(GetByte_Unchecked(sink, getters, ordinal)); } - object result = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == result) + object result = GetSqlValue(sink, getters, ordinal, metaData); + if (result == null) { throw ADP.InvalidCast(); } return (SqlByte)result; } - internal static SqlBytes GetSqlBytes(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData -#if NETFRAMEWORK - , SmiContext context -#endif - ) + internal static SqlBytes GetSqlBytes(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData, SmiContext context) { SqlBytes result; if (CanAccessGetterDirectly(metaData, ExtendedClrTypeCode.SqlBytes)) @@ -497,7 +437,7 @@ internal static SqlBytes GetSqlBytes(SmiEventSink_Default sink, ITypedGettersV3 else { long length = GetBytesLength_Unchecked(sink, getters, ordinal); - if (0 <= length && length < __maxByteChunkSize) + if (length >= 0 && length < MaxByteChunkSize) { byte[] byteBuffer = GetByteArray_Unchecked(sink, getters, ordinal); result = new SqlBytes(byteBuffer); @@ -505,23 +445,15 @@ internal static SqlBytes GetSqlBytes(SmiEventSink_Default sink, ITypedGettersV3 else { Stream s = new SmiGettersStream(sink, getters, ordinal, metaData); - s = CopyIntoNewSmiScratchStream(s, sink -#if NETFRAMEWORK - , context -#endif - ); + s = CopyIntoNewSmiScratchStream(s, sink, context); result = new SqlBytes(s); } } } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -539,11 +471,7 @@ internal static SqlBytes GetSqlBytes(SmiEventSink_Default sink, ITypedGettersV3 return result; } - internal static SqlChars GetSqlChars(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData -#if NETFRAMEWORK - , SmiContext context -#endif - ) + internal static SqlChars GetSqlChars(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData, SmiContext context) { SqlChars result; if (CanAccessGetterDirectly(metaData, ExtendedClrTypeCode.SqlChars)) @@ -556,7 +484,7 @@ internal static SqlChars GetSqlChars(SmiEventSink_Default sink, ITypedGettersV3 { #if NETFRAMEWORK long length = GetCharsLength_Unchecked(sink, getters, ordinal); - if (length < __maxCharChunkSize || !InOutOfProcHelper.InProc) + if (length < MaxByteChunkSize || !InOutOfProcHelper.InProc) { char[] charBuffer = GetCharArray_Unchecked(sink, getters, ordinal); result = new SqlChars(charBuffer); @@ -581,13 +509,9 @@ internal static SqlChars GetSqlChars(SmiEventSink_Default sink, ITypedGettersV3 else { SqlString stringValue; - if (SqlDbType.Xml == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Xml) { - SqlXml xmlValue = GetSqlXml_Unchecked(sink, getters, ordinal -#if NETFRAMEWORK - , null -#endif - ); + SqlXml xmlValue = GetSqlXml_Unchecked(sink, getters, ordinal, null); if (xmlValue.IsNull) { @@ -600,12 +524,8 @@ internal static SqlChars GetSqlChars(SmiEventSink_Default sink, ITypedGettersV3 } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -642,12 +562,8 @@ internal static SqlDateTime GetSqlDateTime(SmiEventSink_Default sink, ITypedGett } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -673,12 +589,8 @@ internal static SqlDecimal GetSqlDecimal(SmiEventSink_Default sink, ITypedGetter } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -705,12 +617,8 @@ internal static SqlDouble GetSqlDouble(SmiEventSink_Default sink, ITypedGettersV } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -737,12 +645,8 @@ internal static SqlGuid GetSqlGuid(SmiEventSink_Default sink, ITypedGettersV3 ge } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -769,12 +673,8 @@ internal static SqlInt16 GetSqlInt16(SmiEventSink_Default sink, ITypedGettersV3 } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -801,12 +701,8 @@ internal static SqlInt32 GetSqlInt32(SmiEventSink_Default sink, ITypedGettersV3 } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -832,12 +728,8 @@ internal static SqlInt64 GetSqlInt64(SmiEventSink_Default sink, ITypedGettersV3 } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -863,12 +755,8 @@ internal static SqlMoney GetSqlMoney(SmiEventSink_Default sink, ITypedGettersV3 } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -895,12 +783,8 @@ internal static SqlSingle GetSqlSingle(SmiEventSink_Default sink, ITypedGettersV } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -927,11 +811,7 @@ internal static SqlString GetSqlString(SmiEventSink_Default sink, ITypedGettersV } else if (SqlDbType.Xml == metaData.SqlDbType) { - SqlXml xmlValue = GetSqlXml_Unchecked(sink, getters, ordinal -#if NETFRAMEWORK - , null -#endif - ); + SqlXml xmlValue = GetSqlXml_Unchecked(sink, getters, ordinal, null); if (xmlValue.IsNull) { @@ -944,12 +824,8 @@ internal static SqlString GetSqlString(SmiEventSink_Default sink, ITypedGettersV } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -959,11 +835,7 @@ internal static SqlString GetSqlString(SmiEventSink_Default sink, ITypedGettersV return result; } - internal static SqlXml GetSqlXml(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData -#if NETFRAMEWORK - , SmiContext context -#endif - ) + internal static SqlXml GetSqlXml(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData, SmiContext context) { SqlXml result; if (CanAccessGetterDirectly(metaData, ExtendedClrTypeCode.SqlXml)) @@ -974,21 +846,13 @@ internal static SqlXml GetSqlXml(SmiEventSink_Default sink, ITypedGettersV3 gett } else { - result = GetSqlXml_Unchecked(sink, getters, ordinal -#if NETFRAMEWORK - , context -#endif - ); + result = GetSqlXml_Unchecked(sink, getters, ordinal, context); } } else { - object obj = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetSqlValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -1005,12 +869,8 @@ internal static string GetString(SmiEventSink_Default sink, ITypedGettersV3 gett { return GetString_Unchecked(sink, getters, ordinal); } - object obj = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); - if (null == obj) + object obj = GetValue(sink, getters, ordinal, metaData); + if (obj == null) { throw ADP.InvalidCast(); } @@ -1025,11 +885,7 @@ internal static TimeSpan GetTimeSpan(SmiEventSink_Default sink, SmiTypedGetterSe { return GetTimeSpan_Unchecked(sink, getters, ordinal); } - return (TimeSpan)GetValue200(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , null -#endif - ); + return (TimeSpan)GetValue200(sink, getters, ordinal, metaData, null); } // GetValue() for v200 SMI (2008 Date/Time types) @@ -1037,13 +893,11 @@ internal static object GetValue200( SmiEventSink_Default sink, SmiTypedGetterSetter getters, int ordinal, - SmiMetaData metaData -#if NETFRAMEWORK - ,SmiContext context -#endif - ) + SmiMetaData metaData, + SmiContext context + ) { - object result = null; + object result; if (IsDBNull_Unchecked(sink, getters, ordinal)) { result = DBNull.Value; @@ -1056,11 +910,7 @@ SmiMetaData metaData metaData = getters.GetVariantType(sink, ordinal); sink.ProcessMessagesAndThrow(); Debug.Assert(SqlDbType.Variant != metaData.SqlDbType, "Variant-within-variant causes endless recursion!"); - result = GetValue200(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetValue200(sink, getters, ordinal, metaData, context); break; case SqlDbType.Date: case SqlDbType.DateTime2: @@ -1073,11 +923,7 @@ SmiMetaData metaData result = GetDateTimeOffset_Unchecked(sink, getters, ordinal); break; default: - result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetValue(sink, getters, ordinal, metaData, context); break; } } @@ -1085,16 +931,16 @@ SmiMetaData metaData return result; } + + // implements SqlClient 1.1-compatible GetValue() semantics for everything except output parameters internal static object GetValue( SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, - SmiMetaData metaData -#if NETFRAMEWORK - ,SmiContext context -#endif - ) + SmiMetaData metaData, + SmiContext context = null + ) { object result = null; if (IsDBNull_Unchecked(sink, getters, ordinal)) @@ -1178,18 +1024,10 @@ SmiMetaData metaData metaData = getters.GetVariantType(sink, ordinal); sink.ProcessMessagesAndThrow(); Debug.Assert(SqlDbType.Variant != metaData.SqlDbType, "Variant-within-variant causes endless recursion!"); - result = GetValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetValue(sink, getters, ordinal, metaData, context); break; case SqlDbType.Xml: - result = GetSqlXml_Unchecked(sink, getters, ordinal -#if NETFRAMEWORK - , context -#endif - ).Value; + result = GetSqlXml_Unchecked(sink, getters, ordinal, context).Value; break; case SqlDbType.Udt: result = GetUdt_LengthChecked(sink, getters, ordinal, metaData); @@ -1205,16 +1043,14 @@ internal static object GetSqlValue200( SmiEventSink_Default sink, SmiTypedGetterSetter getters, int ordinal, - SmiMetaData metaData -#if NETFRAMEWORK - ,SmiContext context -#endif - ) + SmiMetaData metaData, + SmiContext context = null + ) { - object result = null; + object result; if (IsDBNull_Unchecked(sink, getters, ordinal)) { - if (SqlDbType.Udt == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Udt) { result = NullUdtInstance(metaData); } @@ -1231,11 +1067,7 @@ SmiMetaData metaData metaData = getters.GetVariantType(sink, ordinal); sink.ProcessMessagesAndThrow(); Debug.Assert(SqlDbType.Variant != metaData.SqlDbType, "Variant-within-variant causes endless recursion!"); - result = GetSqlValue200(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetSqlValue200(sink, getters, ordinal, metaData, context); break; case SqlDbType.Date: case SqlDbType.DateTime2: @@ -1248,11 +1080,7 @@ SmiMetaData metaData result = GetDateTimeOffset_Unchecked(sink, getters, ordinal); break; default: - result = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetSqlValue(sink, getters, ordinal, metaData, context); break; } } @@ -1265,16 +1093,14 @@ internal static object GetSqlValue( SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, - SmiMetaData metaData -#if NETFRAMEWORK - ,SmiContext context -#endif - ) + SmiMetaData metaData, + SmiContext context = null + ) { object result = null; if (IsDBNull_Unchecked(sink, getters, ordinal)) { - if (SqlDbType.Udt == metaData.SqlDbType) + if ( metaData.SqlDbType ==SqlDbType.Udt) { result = NullUdtInstance(metaData); } @@ -1360,18 +1186,10 @@ SmiMetaData metaData metaData = getters.GetVariantType(sink, ordinal); sink.ProcessMessagesAndThrow(); Debug.Assert(SqlDbType.Variant != metaData.SqlDbType, "Variant-within-variant causes endless recursion!"); - result = GetSqlValue(sink, getters, ordinal, metaData -#if NETFRAMEWORK - , context -#endif - ); + result = GetSqlValue(sink, getters, ordinal, metaData, context); break; case SqlDbType.Xml: - result = GetSqlXml_Unchecked(sink, getters, ordinal -#if NETFRAMEWORK - , context -#endif - ); + result = GetSqlXml_Unchecked(sink, getters, ordinal, context); break; case SqlDbType.Udt: result = GetUdt_LengthChecked(sink, getters, ordinal, metaData); @@ -1423,15 +1241,15 @@ SmiMetaData metaData internal static object NullUdtInstance(SmiMetaData metaData) { - Type t = metaData.Type; - Debug.Assert(t != null, "Unexpected null of Udt type on NullUdtInstance!"); - return t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, new object[] { }, CultureInfo.InvariantCulture); + Type type = metaData.Type; + Debug.Assert(type != null, "Unexpected null of Udt type on NullUdtInstance!"); + return type.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, Array.Empty(), CultureInfo.InvariantCulture); } // Strongly-typed setters are a bit simpler than their corresponding getters. // 1) check to make sure the type is compatible (exception if not) // 2) push the data - internal static void SetDBNull(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, bool value) + internal static void SetDBNull(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal) { SetDBNull_Unchecked(sink, setters, ordinal); } @@ -1439,27 +1257,25 @@ internal static void SetDBNull(SmiEventSink_Default sink, ITypedSettersV3 setter internal static void SetBoolean(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, bool value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Boolean); - SetBoolean_Unchecked(sink, setters, ordinal, value); } internal static void SetByte(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, byte value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Byte); - SetByte_Unchecked(sink, setters, ordinal, value); } internal static long SetBytes(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, long fieldOffset, byte[] buffer, int bufferOffset, int length) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.ByteArray); - if (null == buffer) + if (buffer == null) { throw ADP.ArgumentNull(nameof(buffer)); } - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, fieldOffset, buffer.Length, bufferOffset, length); + length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: fieldOffset, bufferLength: buffer.Length, bufferOffset: bufferOffset, length: length); Debug.Assert(length >= 0, "Buffer.Length was invalid!"); - if (0 == length) + if (length == 0) { // Front end semantics says to ignore fieldOffset and bufferOffset // if not doing any actual work. @@ -1494,13 +1310,13 @@ internal static long SetBytesLength(SmiEventSink_Default sink, ITypedSettersV3 s internal static long SetChars(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, long fieldOffset, char[] buffer, int bufferOffset, int length) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.CharArray); - if (null == buffer) + if (buffer == null) { throw ADP.ArgumentNull(nameof(buffer)); } - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, fieldOffset, buffer.Length, bufferOffset, length); + length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: fieldOffset, bufferLength: buffer.Length, bufferOffset: bufferOffset, length: length); Debug.Assert(length >= 0, "Buffer.Length was invalid!"); - if (0 == length) + if (length == 0) { // Front end semantics says to ignore fieldOffset and bufferOffset // if not doing any actual work. @@ -1515,22 +1331,15 @@ internal static long SetChars(SmiEventSink_Default sink, ITypedSettersV3 setters internal static void SetDateTime(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTime value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.DateTime); - SetDateTime_Checked(sink, setters, ordinal, metaData, value); } - internal static void SetDateTimeOffset(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTimeOffset value -#if NETFRAMEWORK - , bool settersSupport2008DateTime -#endif - ) + internal static void SetDateTimeOffset(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTimeOffset value, bool settersSupport2008DateTime = true) { -#if NETFRAMEWORK if (!settersSupport2008DateTime) { throw ADP.InvalidCast(); } -#endif ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.DateTimeOffset); SetDateTimeOffset_Unchecked(sink, (SmiTypedGetterSetter)setters, ordinal, value); } @@ -1538,49 +1347,42 @@ internal static void SetDateTimeOffset(SmiEventSink_Default sink, ITypedSettersV internal static void SetDecimal(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, decimal value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Decimal); - SetDecimal_PossiblyMoney(sink, setters, ordinal, metaData, value); } internal static void SetDouble(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, double value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Double); - SetDouble_Unchecked(sink, setters, ordinal, value); } internal static void SetGuid(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, Guid value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Guid); - SetGuid_Unchecked(sink, setters, ordinal, value); } internal static void SetInt16(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, short value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Int16); - SetInt16_Unchecked(sink, setters, ordinal, value); } internal static void SetInt32(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, int value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Int32); - SetInt32_Unchecked(sink, setters, ordinal, value); } internal static void SetInt64(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, long value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Int64); - SetInt64_Unchecked(sink, setters, ordinal, value); } internal static void SetSingle(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, float value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.Single); - SetSingle_Unchecked(sink, setters, ordinal, value); } @@ -1593,7 +1395,6 @@ internal static void SetSqlBinary(SmiEventSink_Default sink, ITypedSettersV3 set internal static void SetSqlBoolean(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlBoolean value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlBoolean); - SetSqlBoolean_Unchecked(sink, setters, ordinal, value); } @@ -1607,7 +1408,6 @@ internal static void SetSqlByte(SmiEventSink_Default sink, ITypedSettersV3 sette internal static void SetSqlBytes(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlBytes value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlBytes); - SetSqlBytes_LengthChecked(sink, setters, ordinal, metaData, value, 0); } @@ -1620,63 +1420,54 @@ internal static void SetSqlChars(SmiEventSink_Default sink, ITypedSettersV3 sett internal static void SetSqlDateTime(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlDateTime value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlDateTime); - SetSqlDateTime_Checked(sink, setters, ordinal, metaData, value); } internal static void SetSqlDecimal(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlDecimal value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlDecimal); - SetSqlDecimal_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlDouble(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlDouble value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlDouble); - SetSqlDouble_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlGuid(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlGuid value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlGuid); - SetSqlGuid_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlInt16(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlInt16 value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlInt16); - SetSqlInt16_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlInt32(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlInt32 value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlInt32); - SetSqlInt32_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlInt64(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlInt64 value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlInt64); - SetSqlInt64_Unchecked(sink, setters, ordinal, value); } internal static void SetSqlMoney(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlMoney value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlMoney); - SetSqlMoney_Checked(sink, setters, ordinal, metaData, value); } internal static void SetSqlSingle(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlSingle value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlSingle); - SetSqlSingle_Unchecked(sink, setters, ordinal, value); } @@ -1689,29 +1480,21 @@ internal static void SetSqlString(SmiEventSink_Default sink, ITypedSettersV3 set internal static void SetSqlXml(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlXml value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.SqlXml); - SetSqlXml_Unchecked(sink, setters, ordinal, value); } internal static void SetString(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, string value) { ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.String); - SetString_LengthChecked(sink, setters, ordinal, metaData, value, 0); } - internal static void SetTimeSpan(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, TimeSpan value -#if NETFRAMEWORK - , bool settersSupport2008DateTime -#endif - ) + internal static void SetTimeSpan(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, TimeSpan value, bool settersSupport2008DateTime = true) { -#if NETFRAMEWORK if (!settersSupport2008DateTime) { throw ADP.InvalidCast(); } -#endif ThrowIfInvalidSetterAccess(metaData, ExtendedClrTypeCode.TimeSpan); SetTimeSpan_Checked(sink, (SmiTypedGetterSetter)setters, ordinal, metaData, value); } @@ -1726,19 +1509,21 @@ internal static void SetCompatibleValue( object value, ExtendedClrTypeCode typeCode, int offset - ) + ) { // Ensure either an invalid type, or caller validated compatibility // SqlDbType.Variant and have special handling - Debug.Assert(typeCode == ExtendedClrTypeCode.Invalid || - typeCode == ExtendedClrTypeCode.SByte || - typeCode == ExtendedClrTypeCode.UInt16 || - typeCode == ExtendedClrTypeCode.UInt32 || - typeCode == ExtendedClrTypeCode.UInt64 || - typeCode == ExtendedClrTypeCode.DBNull || - typeCode == ExtendedClrTypeCode.Empty || - CanAccessSetterDirectly(metaData, typeCode) || - value is DataFeed /* already validated */); + Debug.Assert( + typeCode == ExtendedClrTypeCode.Invalid || + typeCode == ExtendedClrTypeCode.SByte || + typeCode == ExtendedClrTypeCode.UInt16 || + typeCode == ExtendedClrTypeCode.UInt32 || + typeCode == ExtendedClrTypeCode.UInt64 || + typeCode == ExtendedClrTypeCode.DBNull || + typeCode == ExtendedClrTypeCode.Empty || + CanAccessSetterDirectly(metaData, typeCode) || + value is DataFeed + ); switch (typeCode) { @@ -1880,16 +1665,20 @@ internal static void SetCompatibleValueV200( object value, ExtendedClrTypeCode typeCode, int offset, - int length, ParameterPeekAheadValue peekAhead, SqlBuffer.StorageType storageType - ) + ) { // Ensure caller validated compatibility for types handled directly in this method - Debug.Assert((ExtendedClrTypeCode.DataTable != typeCode && - ExtendedClrTypeCode.DbDataReader != typeCode && - ExtendedClrTypeCode.IEnumerableOfSqlDataRecord != typeCode) || - CanAccessSetterDirectly(metaData, typeCode), "Un-validated type '" + typeCode + "' for metaData: " + metaData.SqlDbType); + Debug.Assert( + ( + typeCode != ExtendedClrTypeCode.DataTable && + typeCode != ExtendedClrTypeCode.DbDataReader && + typeCode != ExtendedClrTypeCode.IEnumerableOfSqlDataRecord + ) || + CanAccessSetterDirectly(metaData, typeCode), + "Un-validated type '" + typeCode + "' for metaData: " + metaData.SqlDbType + ); if (typeCode == ExtendedClrTypeCode.DateTime) { @@ -1902,7 +1691,7 @@ SqlBuffer.StorageType storageType } else { - SetCompatibleValueV200(sink, setters, ordinal, metaData, value, typeCode, offset, length, peekAhead); + SetCompatibleValueV200(sink, setters, ordinal, metaData, value, typeCode, offset, peekAhead); } } @@ -1917,15 +1706,19 @@ internal static void SetCompatibleValueV200( object value, ExtendedClrTypeCode typeCode, int offset, - int length, ParameterPeekAheadValue peekAhead - ) + ) { // Ensure caller validated compatibility for types handled directly in this method - Debug.Assert((ExtendedClrTypeCode.DataTable != typeCode && - ExtendedClrTypeCode.DbDataReader != typeCode && - ExtendedClrTypeCode.IEnumerableOfSqlDataRecord != typeCode) || - CanAccessSetterDirectly(metaData, typeCode), "Un-validated type '" + typeCode + "' for metaData: " + metaData.SqlDbType); + Debug.Assert( + ( + typeCode != ExtendedClrTypeCode.DataTable && + typeCode != ExtendedClrTypeCode.DbDataReader && + typeCode != ExtendedClrTypeCode.IEnumerableOfSqlDataRecord + ) || + CanAccessSetterDirectly(metaData, typeCode), + "Un-validated type '" + typeCode + "' for metaData: " + metaData.SqlDbType + ); switch (typeCode) { @@ -2224,9 +2017,13 @@ internal static void FillCompatibleSettersFromReader(SmiEventSink_Default sink, #endif ); if ((storageType == SqlBuffer.StorageType.DateTime2) || (storageType == SqlBuffer.StorageType.Date)) - SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, 0, null, storageType); + { + SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, null, storageType); + } else - SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, 0, null); + { + SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, null); + } } break; @@ -2244,7 +2041,7 @@ internal static void FillCompatibleSettersFromReader(SmiEventSink_Default sink, SetDateTime_Checked(sink, setters, i, metaData[i], reader.GetDateTime(i)); break; case SqlDbType.Time: - { // block to scope sqlReader local and avoid conflicts + { Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.TimeSpan)); TimeSpan ts; if (reader is SqlDataReader sqlReader) @@ -2259,7 +2056,7 @@ internal static void FillCompatibleSettersFromReader(SmiEventSink_Default sink, } break; case SqlDbType.DateTimeOffset: - { // block to scope sqlReader local and avoid conflicts + { Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.DateTimeOffset)); DateTimeOffset dto; if (reader is SqlDataReader sqlReader) @@ -2290,7 +2087,7 @@ internal static void FillCompatibleSettersFromRecord(SmiEventSink_Default sink, { for (int i = 0; i < metaData.Length; ++i) { - if (null != useDefaultValues && useDefaultValues[i]) + if (useDefaultValues != null && useDefaultValues[i]) { continue; } @@ -2395,7 +2192,7 @@ internal static void FillCompatibleSettersFromRecord(SmiEventSink_Default sink, case SqlDbType.Variant: object o = record.GetSqlValue(i); ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCode(o); - SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, -1 /* no length restriction */, null /* no peekahead */); + SetCompatibleValueV200(sink, setters, i, metaData[i], o, typeCode, 0, null /* no peekahead */); break; case SqlDbType.Udt: Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.SqlBytes)); @@ -2407,7 +2204,7 @@ internal static void FillCompatibleSettersFromRecord(SmiEventSink_Default sink, SetDateTime_Checked(sink, setters, i, metaData[i], record.GetDateTime(i)); break; case SqlDbType.Time: - { // block to scope sqlReader local and avoid conflicts + { Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.TimeSpan)); TimeSpan ts; if (record is SqlDataRecord sqlRecord) @@ -2422,7 +2219,7 @@ internal static void FillCompatibleSettersFromRecord(SmiEventSink_Default sink, } break; case SqlDbType.DateTimeOffset: - { // block to scope sqlReader local and avoid conflicts + { Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.DateTimeOffset)); DateTimeOffset dto; if (record is SqlDataRecord sqlRecord) @@ -2453,67 +2250,63 @@ private static object GetUdt_LengthChecked(SmiEventSink_Default sink, ITypedGett object result; if (IsDBNull_Unchecked(sink, getters, ordinal)) { - Type t = metaData.Type; - Debug.Assert(t != null, "Unexpected null of udtType on GetUdt_LengthChecked!"); - result = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, Array.Empty(), CultureInfo.InvariantCulture); + Type type = metaData.Type; + Debug.Assert(type != null, "Unexpected null of udtType on GetUdt_LengthChecked!"); + result = type.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, Array.Empty(), CultureInfo.InvariantCulture); Debug.Assert(result != null); } else { // Note: do not need to copy getter stream, since it will not be used beyond // deserialization (valid lifetime of getters is limited). - Stream s = new SmiGettersStream(sink, getters, ordinal, metaData); - result = SerializationHelperSql9.Deserialize(s, metaData.Type); + Stream stream = new SmiGettersStream(sink, getters, ordinal, metaData); + result = SerializationHelperSql9.Deserialize(stream, metaData.Type); } return result; } private static decimal GetDecimal_PossiblyMoney(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiMetaData metaData) { - if (SqlDbType.Decimal == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Decimal) { return GetSqlDecimal_Unchecked(sink, getters, ordinal).Value; } else { - Debug.Assert(SqlDbType.Money == metaData.SqlDbType || - SqlDbType.SmallMoney == metaData.SqlDbType, - "Unexpected sqldbtype=" + metaData.SqlDbType); + Debug.Assert(metaData.SqlDbType == SqlDbType.Money || metaData.SqlDbType == SqlDbType.SmallMoney, "Unexpected sqldbtype=" + metaData.SqlDbType); return GetSqlMoney_Unchecked(sink, getters, ordinal).Value; } } private static void SetDecimal_PossiblyMoney(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, decimal value) { - if (SqlDbType.Decimal == metaData.SqlDbType || SqlDbType.Variant == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Decimal || metaData.SqlDbType == SqlDbType.Variant) { SetDecimal_Unchecked(sink, setters, ordinal, value); } else { - Debug.Assert(SqlDbType.Money == metaData.SqlDbType || - SqlDbType.SmallMoney == metaData.SqlDbType, - "Unexpected sqldbtype=" + metaData.SqlDbType); + Debug.Assert(metaData.SqlDbType == SqlDbType.Money || metaData.SqlDbType == SqlDbType.SmallMoney, "Unexpected sqldbtype=" + metaData.SqlDbType); SetSqlMoney_Checked(sink, setters, ordinal, metaData, new SqlMoney(value)); } } // Hard coding smalldatetime limits... - private static readonly DateTime s_dtSmallMax = new(2079, 06, 06, 23, 59, 29, 998); - private static readonly DateTime s_dtSmallMin = new(1899, 12, 31, 23, 59, 29, 999); + private static readonly DateTime s_smallDateTimeMax = new DateTime(2079, 06, 06, 23, 59, 29, 998); + private static readonly DateTime s_smallDateTimeMin = new DateTime(1899, 12, 31, 23, 59, 29, 999); private static void VerifyDateTimeRange(SqlDbType dbType, DateTime value) { - if (SqlDbType.SmallDateTime == dbType && (s_dtSmallMax < value || s_dtSmallMin > value)) + if (dbType == SqlDbType.SmallDateTime && (s_smallDateTimeMax < value || s_smallDateTimeMin > value)) { throw ADP.InvalidMetaDataValue(); } } - private static readonly TimeSpan s_timeMin = TimeSpan.Zero; - private static readonly TimeSpan s_timeMax = new(TimeSpan.TicksPerDay - 1); + private static readonly TimeSpan s_timeSpanMin = TimeSpan.Zero; + private static readonly TimeSpan s_timeSpanMax = new TimeSpan(TimeSpan.TicksPerDay - 1); private static void VerifyTimeRange(SqlDbType dbType, TimeSpan value) { - if (SqlDbType.Time == dbType && (s_timeMin > value || value > s_timeMax)) + if (dbType == SqlDbType.Time && (s_timeSpanMin > value || value > s_timeSpanMax)) { throw ADP.InvalidMetaDataValue(); } @@ -2522,7 +2315,7 @@ private static void VerifyTimeRange(SqlDbType dbType, TimeSpan value) private static void SetDateTime_Checked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTime value) { VerifyDateTimeRange(metaData.SqlDbType, value); - SetDateTime_Unchecked(sink, setters, ordinal, ((SqlDbType.Date == metaData.SqlDbType) ? value.Date : value)); + SetDateTime_Unchecked(sink, setters, ordinal, (metaData.SqlDbType == SqlDbType.Date) ? value.Date : value); } private static void SetTimeSpan_Checked(SmiEventSink_Default sink, SmiTypedGetterSetter setters, int ordinal, SmiMetaData metaData, TimeSpan value) @@ -2554,10 +2347,10 @@ private static void SetDate_Checked(SmiEventSink_Default sink, ITypedSettersV3 s private static void SetSqlMoney_Checked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlMoney value) { - if (!value.IsNull && SqlDbType.SmallMoney == metaData.SqlDbType) + if (!value.IsNull && metaData.SqlDbType == SqlDbType.SmallMoney) { decimal decimalValue = value.Value; - if (TdsEnums.SQL_SMALL_MONEY_MIN > decimalValue || TdsEnums.SQL_SMALL_MONEY_MAX < decimalValue) + if (decimalValue < TdsEnums.SQL_SMALL_MONEY_MIN || decimalValue > TdsEnums.SQL_SMALL_MONEY_MAX) { throw SQL.MoneyOverflow(decimalValue.ToString(CultureInfo.InvariantCulture)); } @@ -2567,14 +2360,14 @@ private static void SetSqlMoney_Checked(SmiEventSink_Default sink, ITypedSetters private static void SetByteArray_LengthChecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, byte[] buffer, int offset) { - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, buffer.Length, offset, buffer.Length - offset); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: buffer.Length, bufferOffset: offset, length: buffer.Length - offset); Debug.Assert(length >= 0, "buffer.Length was invalid!"); SetByteArray_Unchecked(sink, setters, ordinal, buffer, offset, length); } private static void SetCharArray_LengthChecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, char[] buffer, int offset) { - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, buffer.Length, offset, buffer.Length - offset); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: buffer.Length, bufferOffset: offset, length: buffer.Length - offset); Debug.Assert(length >= 0, "buffer.Length was invalid!"); SetCharArray_Unchecked(sink, setters, ordinal, buffer, offset, length); } @@ -2584,7 +2377,7 @@ private static void SetSqlBinary_LengthChecked(SmiEventSink_Default sink, ITyped int length = 0; if (!value.IsNull) { - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, value.Length, offset, value.Length - offset); + length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: value.Length, bufferOffset: offset, length: value.Length - offset); Debug.Assert(length >= 0, "value.Length was invalid!"); } SetSqlBinary_Unchecked(sink, setters, ordinal, value, offset, length); @@ -2592,7 +2385,6 @@ private static void SetSqlBinary_LengthChecked(SmiEventSink_Default sink, ITyped private static void SetBytes_FromRecord(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlDataRecord record, int offset) { - // Deal with large values by sending bufferLength of NoLengthLimit (== assume // CheckXetParameters will ignore requested-length checks in this case long bufferLength = record.GetBytes(ordinal, 0, null, 0, 0); @@ -2600,12 +2392,12 @@ private static void SetBytes_FromRecord(SmiEventSink_Default sink, ITypedSetters { bufferLength = NoLengthLimit; } - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, checked((int)bufferLength), offset, checked((int)bufferLength)); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: checked((int)bufferLength), bufferOffset: offset, length: checked((int)bufferLength)); int chunkSize; - if (length > __maxByteChunkSize || length < 0) + if (length > MaxByteChunkSize || length < 0) { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } else { @@ -2618,18 +2410,17 @@ private static void SetBytes_FromRecord(SmiEventSink_Default sink, ITypedSetters long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (bytesRead = record.GetBytes(ordinal, currentOffset, buffer, 0, chunkSize)) && - 0 != bytesWritten) + while ( + (length < 0 || lengthWritten < length) && + (bytesRead = record.GetBytes(ordinal, currentOffset, buffer, 0, chunkSize)) != 0 && + bytesWritten != 0 + ) { bytesWritten = setters.SetBytes(sink, ordinal, currentOffset, buffer, 0, checked((int)bytesRead)); sink.ProcessMessagesAndThrow(); checked { currentOffset += bytesWritten; - } - checked - { lengthWritten += bytesWritten; } } @@ -2641,13 +2432,12 @@ private static void SetBytes_FromRecord(SmiEventSink_Default sink, ITypedSetters private static void SetBytes_FromReader(SmiEventSink_Default sink, SmiTypedGetterSetter setters, int ordinal, SmiMetaData metaData, DbDataReader reader, int offset) { - // Deal with large values by sending bufferLength of NoLengthLimit (== assume // CheckXetParameters will ignore requested-length checks in this case) - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, NoLengthLimit /* buffer length */, offset, NoLengthLimit /* requested length */ ); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: NoLengthLimit, bufferOffset: offset, length: NoLengthLimit); // Use fixed chunk size for all cases to avoid inquiring from reader. - int chunkSize = __maxByteChunkSize; + int chunkSize = MaxByteChunkSize; byte[] buffer = new byte[chunkSize]; long bytesRead; @@ -2655,18 +2445,17 @@ private static void SetBytes_FromReader(SmiEventSink_Default sink, SmiTypedGette long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (bytesRead = reader.GetBytes(ordinal, currentOffset, buffer, 0, chunkSize)) && - 0 != bytesWritten) + while ( + (length < 0 || lengthWritten < length) && + (bytesRead = reader.GetBytes(ordinal, currentOffset, buffer, 0, chunkSize)) != 0 && + bytesWritten != 0 + ) { bytesWritten = setters.SetBytes(sink, ordinal, currentOffset, buffer, 0, checked((int)bytesRead)); sink.ProcessMessagesAndThrow(); checked { currentOffset += bytesWritten; - } - checked - { lengthWritten += bytesWritten; } } @@ -2688,14 +2477,13 @@ private static void SetSqlBytes_LengthChecked(SmiEventSink_Default sink, ITypedS { bufferLength = NoLengthLimit; } - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, checked((int)bufferLength), offset, checked((int)bufferLength)); + length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: checked((int)bufferLength), bufferOffset: offset, length: checked((int)bufferLength)); } SetSqlBytes_Unchecked(sink, setters, ordinal, value, 0, length); } private static void SetChars_FromRecord(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, SqlDataRecord record, int offset) { - // Deal with large values by sending bufferLength of NoLengthLimit // CheckXetParameters will ignore length checks in this case long bufferLength = record.GetChars(ordinal, 0, null, 0, 0); @@ -2703,18 +2491,18 @@ private static void SetChars_FromRecord(SmiEventSink_Default sink, ITypedSetters { bufferLength = NoLengthLimit; } - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, checked((int)bufferLength), offset, checked((int)bufferLength - offset)); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: checked((int)bufferLength), bufferOffset: offset, length: checked((int)bufferLength - offset)); int chunkSize; - if (length > __maxCharChunkSize || length < 0) + if (length > MaxCharChunkSize || length < 0) { if (MetaDataUtilsSmi.IsAnsiType(metaData.SqlDbType)) { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } else { - chunkSize = __maxCharChunkSize; + chunkSize = MaxCharChunkSize; } } else @@ -2728,18 +2516,17 @@ private static void SetChars_FromRecord(SmiEventSink_Default sink, ITypedSetters long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (charsRead = record.GetChars(ordinal, currentOffset, buffer, 0, chunkSize)) && - 0 != charsWritten) + while ( + (length < 0 || lengthWritten < length) && + (charsRead = record.GetChars(ordinal, currentOffset, buffer, 0, chunkSize)) != 0 && + charsWritten != 0 + ) { charsWritten = setters.SetChars(sink, ordinal, currentOffset, buffer, 0, checked((int)charsRead)); sink.ProcessMessagesAndThrow(); checked { currentOffset += charsWritten; - } - checked - { lengthWritten += charsWritten; } } @@ -2777,20 +2564,19 @@ private static void SetCharsOrString_FromReader(SmiEventSink_Default sink, SmiTy // Use chunking via SetChars to transfer a value from a reader to a gettersetter private static void SetChars_FromReader(SmiEventSink_Default sink, SmiTypedGetterSetter setters, int ordinal, SmiMetaData metaData, DbDataReader reader, int offset) { - // Deal with large values by sending bufferLength of NoLengthLimit (== assume // CheckXetParameters will ignore requested-length checks in this case) - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, NoLengthLimit /* buffer length */, offset, NoLengthLimit /* requested length */ ); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: NoLengthLimit , bufferOffset: offset, length: NoLengthLimit ); // Use fixed chunk size for all cases to avoid inquiring from reader. int chunkSize; if (MetaDataUtilsSmi.IsAnsiType(metaData.SqlDbType)) { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } else { - chunkSize = __maxCharChunkSize; + chunkSize = MaxCharChunkSize; } char[] buffer = new char[chunkSize]; @@ -2799,18 +2585,17 @@ private static void SetChars_FromReader(SmiEventSink_Default sink, SmiTypedGette long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (charsRead = reader.GetChars(ordinal, currentOffset, buffer, 0, chunkSize)) && - 0 != charsWritten) + while ( + (length < 0 || lengthWritten < length) && + (charsRead = reader.GetChars(ordinal, currentOffset, buffer, 0, chunkSize)) != 0 && + charsWritten != 0 + ) { charsWritten = setters.SetChars(sink, ordinal, currentOffset, buffer, 0, checked((int)charsRead)); sink.ProcessMessagesAndThrow(); checked { currentOffset += charsWritten; - } - checked - { lengthWritten += charsWritten; } } @@ -2823,7 +2608,7 @@ private static void SetChars_FromReader(SmiEventSink_Default sink, SmiTypedGette private static void SetString_FromReader(SmiEventSink_Default sink, SmiTypedGetterSetter setters, int ordinal, SmiMetaData metaData, DbDataReader reader, int offset) { string value = reader.GetString(ordinal); - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, value.Length, 0, NoLengthLimit /* buffer */, offset, NoLengthLimit /* request */); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, value.Length, fieldOffset: 0, bufferLength: NoLengthLimit, bufferOffset: offset, length: NoLengthLimit); setters.SetString(sink, ordinal, value, offset, length); sink.ProcessMessagesAndThrow(); @@ -2841,7 +2626,7 @@ private static void SetSqlChars_LengthChecked(SmiEventSink_Default sink, ITypedS { bufferLength = NoLengthLimit; } - length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, checked((int)bufferLength), offset, checked((int)bufferLength - offset)); + length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: checked((int)bufferLength), bufferOffset: offset, length: checked((int)bufferLength - offset)); } SetSqlChars_Unchecked(sink, setters, ordinal, value, 0, length); } @@ -2855,7 +2640,7 @@ private static void SetSqlString_LengthChecked(SmiEventSink_Default sink, ITyped else { string stringValue = value.Value; - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, stringValue.Length, offset, stringValue.Length - offset); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, bufferLength: stringValue.Length, bufferOffset: offset, length: stringValue.Length - offset); Debug.Assert(length >= 0, "value.Length was invalid!"); SetSqlString_Unchecked(sink, setters, ordinal, metaData, value, offset, length); } @@ -2863,7 +2648,7 @@ private static void SetSqlString_LengthChecked(SmiEventSink_Default sink, ITyped private static void SetString_LengthChecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, string value, int offset) { - int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, NoLengthLimit /* actual */, 0, value.Length, offset, checked(value.Length - offset)); + int length = CheckXetParameters(metaData.SqlDbType, metaData.MaxLength, actualLength: NoLengthLimit, fieldOffset: 0, value.Length, offset, checked(value.Length - offset)); Debug.Assert(length >= 0, "value.Length was invalid!"); SetString_Unchecked(sink, setters, ordinal, value, offset, length); } @@ -2913,10 +2698,14 @@ private static bool CanAccessGetterDirectly(SmiMetaData metaData, ExtendedClrTyp bool returnValue = s_canAccessGetterDirectly[(int)setterTypeCode, (int)metaData.SqlDbType]; // Additional restrictions to distinguish TVPs and Structured UDTs - if (returnValue && - (ExtendedClrTypeCode.DataTable == setterTypeCode || - ExtendedClrTypeCode.DbDataReader == setterTypeCode || - ExtendedClrTypeCode.IEnumerableOfSqlDataRecord == setterTypeCode)) + if ( + returnValue && + ( + setterTypeCode == ExtendedClrTypeCode.DataTable || + setterTypeCode == ExtendedClrTypeCode.DbDataReader || + setterTypeCode == ExtendedClrTypeCode.IEnumerableOfSqlDataRecord + ) + ) { returnValue = metaData.IsMultiValued; } @@ -2935,10 +2724,14 @@ private static bool CanAccessSetterDirectly(SmiMetaData metaData, ExtendedClrTyp bool returnValue = s_canAccessSetterDirectly[(int)setterTypeCode, (int)metaData.SqlDbType]; // Additional restrictions to distinguish TVPs and Structured UDTs - if (returnValue && - (ExtendedClrTypeCode.DataTable == setterTypeCode || - ExtendedClrTypeCode.DbDataReader == setterTypeCode || - ExtendedClrTypeCode.IEnumerableOfSqlDataRecord == setterTypeCode)) + if ( + returnValue && + ( + setterTypeCode == ExtendedClrTypeCode.DataTable || + setterTypeCode == ExtendedClrTypeCode.DbDataReader || + setterTypeCode == ExtendedClrTypeCode.IEnumerableOfSqlDataRecord + ) + ) { returnValue = metaData.IsMultiValued; } @@ -2963,18 +2756,20 @@ private static long PositiveMin(long first, long second) // Check Get Byte/Chars parameters, throw or adjust invalid values private static int CheckXetParameters( - SqlDbType dbType, - long maxLength, - long actualLength, - long fieldOffset, - int bufferLength, - int bufferOffset, - int length) - { - if (0 > fieldOffset) + SqlDbType dbType, + long maxLength, + long actualLength, + long fieldOffset, + int bufferLength, + int bufferOffset, + int length + ) + { + if (fieldOffset < 0) + { throw ADP.NegativeParameter(nameof(fieldOffset)); + } - // if negative buffer index, throw if (bufferOffset < 0) { throw ADP.InvalidDestinationBufferIndex(bufferLength, bufferOffset, nameof(bufferOffset)); @@ -2991,20 +2786,22 @@ private static int CheckXetParameters( return length; } - // if bad buffer index, throw if (bufferOffset > bufferLength) { throw ADP.InvalidDestinationBufferIndex(bufferLength, bufferOffset, nameof(bufferOffset)); } - // if there is not enough room in the buffer for data if (checked(length + bufferOffset) > bufferLength) + { throw ADP.InvalidBufferSizeOrIndex(length, bufferOffset); + } if (length < 0) + { throw ADP.InvalidDataLength(length); + } - if (0 <= actualLength && actualLength <= fieldOffset) + if (actualLength >=0 && actualLength <= fieldOffset) { return 0; } @@ -3017,21 +2814,20 @@ private static int CheckXetParameters( // special case for variants, since their maxLength is actually a bit bigger than // the actual data length allowed. - if (SqlDbType.Variant == dbType) + if (dbType == SqlDbType.Variant) { length = Math.Min(length, TdsEnums.TYPE_SIZE_LIMIT); } - Debug.Assert(0 > maxLength || 0 > actualLength || - maxLength >= actualLength, "Actual = " + actualLength + ", max = " + maxLength + ", sqldbtype=" + dbType); + Debug.Assert(0 > maxLength || 0 > actualLength || maxLength >= actualLength, "Actual = " + actualLength + ", max = " + maxLength + ", sqldbtype=" + dbType); - if (0 <= actualLength) + if (actualLength >= 0) { // Length is guaranteed to be >= 0 coming in and actualLength >= fieldOffset, so this operation guarantees result >= 0 length = (int)Math.Min((long)length, actualLength - fieldOffset); Debug.Assert(length >= 0, "result < 0, actualLength/fieldOffset problem?"); } - else if (SqlDbType.Udt != dbType && 0 <= maxLength) + else if (dbType != SqlDbType.Udt && maxLength >= 0) { length = (int)Math.Min((long)length, maxLength - fieldOffset); Debug.Assert(length >= 0, "Result < 0, maxlen/fieldoffset problem?"); @@ -3229,7 +3025,6 @@ private static long GetBytesLength_Unchecked(SmiEventSink_Default sink, ITypedGe return result; } - private static char[] GetCharArray_Unchecked(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal) { Debug.Assert(!IsDBNull_Unchecked(sink, getters, ordinal)); @@ -3366,11 +3161,7 @@ private static SqlMoney GetSqlMoney_Unchecked(SmiEventSink_Default sink, ITypedG return SqlTypeWorkarounds.SqlMoneyCtor(temp, 1 /* ignored */ ); } - private static SqlXml GetSqlXml_Unchecked(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal -#if NETFRAMEWORK - , SmiContext context -#endif - ) + private static SqlXml GetSqlXml_Unchecked(SmiEventSink_Default sink, ITypedGettersV3 getters, int ordinal, SmiContext context) { Debug.Assert(!IsDBNull_Unchecked(sink, getters, ordinal)); #if NETFRAMEWORK @@ -3378,7 +3169,7 @@ private static SqlXml GetSqlXml_Unchecked(SmiEventSink_Default sink, ITypedGette // this method without having to pass along the almost-never-used context as a parameter // Looking the context up like this will be slightly slower, but still correct behavior // since it's only used to get a scratch stream. - if (null == context && InOutOfProcHelper.InProc) + if (context == null && InOutOfProcHelper.InProc) { context = SmiContextFactory.Instance.GetCurrentContext(); // In the future we need to push the context checking to a higher level } @@ -3386,11 +3177,7 @@ private static SqlXml GetSqlXml_Unchecked(SmiEventSink_Default sink, ITypedGette // Note: must make a copy of getter stream, since it will be used beyond // this method (valid lifetime of getters is limited). Stream s = new SmiGettersStream(sink, getters, ordinal, SmiMetaData.DefaultXml); - Stream copy = ValueUtilsSmi.CopyIntoNewSmiScratchStream(s, sink -#if NETFRAMEWORK - , context -#endif - ); + Stream copy = ValueUtilsSmi.CopyIntoNewSmiScratchStream(s, sink, context); SqlXml result = new(copy); return result; } @@ -3437,11 +3224,11 @@ private static void SetByteArray_Unchecked(SmiEventSink_Default sink, ITypedSett private static void SetStream_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metadata, StreamDataFeed feed) { long len = metadata.MaxLength; - byte[] buff = new byte[constBinBufferSize]; + byte[] buff = new byte[DefaultBinaryBufferSize]; int nWritten = 0; do { - int readSize = constBinBufferSize; + int readSize = DefaultBinaryBufferSize; if (len > 0 && nWritten + readSize > len) { readSize = (int)(len - nWritten); @@ -3469,11 +3256,11 @@ private static void SetStream_Unchecked(SmiEventSink_Default sink, ITypedSetters private static void SetTextReader_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metadata, TextDataFeed feed) { long len = metadata.MaxLength; - char[] buff = new char[constTextBufferSize]; + char[] buff = new char[DefaultTextBufferSize]; int nWritten = 0; do { - int readSize = constTextBufferSize; + int readSize = DefaultTextBufferSize; if (len > 0 && nWritten + readSize > len) { readSize = (int)(len - nWritten); @@ -3549,7 +3336,7 @@ private static void SetDateTime_Unchecked(SmiEventSink_Default sink, ITypedSette private static void SetDateTime2_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTime value) { - Debug.Assert(SqlDbType.Variant == metaData.SqlDbType, "Invalid type. This should be called only when the type is variant."); + Debug.Assert(metaData.SqlDbType == SqlDbType.Variant, "Invalid type. This should be called only when the type is variant."); setters.SetVariantMetaData(sink, ordinal, SmiMetaData.DefaultDateTime2); setters.SetDateTime(sink, ordinal, value); sink.ProcessMessagesAndThrow(); @@ -3557,7 +3344,7 @@ private static void SetDateTime2_Unchecked(SmiEventSink_Default sink, ITypedSett private static void SetDate_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTime value) { - Debug.Assert(SqlDbType.Variant == metaData.SqlDbType, "Invalid type. This should be called only when the type is variant."); + Debug.Assert(metaData.SqlDbType == SqlDbType.Variant, "Invalid type. This should be called only when the type is variant."); setters.SetVariantMetaData(sink, ordinal, SmiMetaData.DefaultDate); setters.SetDateTime(sink, ordinal, value); sink.ProcessMessagesAndThrow(); @@ -3661,9 +3448,9 @@ private static void SetSqlBytes_Unchecked(SmiEventSink_Default sink, ITypedSette else { int chunkSize; - if (length > __maxByteChunkSize || length < 0) + if (length > MaxByteChunkSize || length < 0) { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } else { @@ -3676,9 +3463,11 @@ private static void SetSqlBytes_Unchecked(SmiEventSink_Default sink, ITypedSette long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (bytesRead = value.Read(currentOffset, buffer, 0, chunkSize)) && - 0 != bytesWritten) + while ( + (length < 0 || lengthWritten < length) && + (bytesRead = value.Read(currentOffset, buffer, 0, chunkSize)) != 0 && + bytesWritten != 0 + ) { bytesWritten = setters.SetBytes(sink, ordinal, currentOffset, buffer, 0, checked((int)bytesRead)); sink.ProcessMessagesAndThrow(); @@ -3708,9 +3497,9 @@ private static void SetSqlChars_Unchecked(SmiEventSink_Default sink, ITypedSette else { int chunkSize; - if (length > __maxCharChunkSize || length < 0) + if (length > MaxCharChunkSize || length < 0) { - chunkSize = __maxCharChunkSize; + chunkSize = MaxCharChunkSize; } else { @@ -3723,18 +3512,17 @@ private static void SetSqlChars_Unchecked(SmiEventSink_Default sink, ITypedSette long currentOffset = offset; long lengthWritten = 0; - while ((length < 0 || lengthWritten < length) && - 0 != (charsRead = value.Read(currentOffset, buffer, 0, chunkSize)) && - 0 != charsWritten) + while ( + (length < 0 || lengthWritten < length) && + (charsRead = value.Read(currentOffset, buffer, 0, chunkSize)) != 0 && + charsWritten != 0 + ) { charsWritten = setters.SetChars(sink, ordinal, currentOffset, buffer, 0, checked((int)charsRead)); sink.ProcessMessagesAndThrow(); checked { currentOffset += charsWritten; - } - checked - { lengthWritten += charsWritten; } } @@ -3844,7 +3632,7 @@ private static void SetSqlMoney_Unchecked(SmiEventSink_Default sink, ITypedSette } else { - if (SqlDbType.Variant == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Variant) { setters.SetVariantMetaData(sink, ordinal, SmiMetaData.DefaultMoney); sink.ProcessMessagesAndThrow(); @@ -3877,17 +3665,18 @@ private static void SetSqlString_Unchecked(SmiEventSink_Default sink, ITypedSett } else { - if (SqlDbType.Variant == metaData.SqlDbType) + if (metaData.SqlDbType == SqlDbType.Variant) { // Set up a NVarChar metadata with correct LCID/Collation metaData = new SmiMetaData( - SqlDbType.NVarChar, - SmiMetaData.MaxUnicodeCharacters, - 0, - 0, - value.LCID, - value.SqlCompareOptions, - null); + SqlDbType.NVarChar, + SmiMetaData.MaxUnicodeCharacters, + precision: 0, + scale: 0, + value.LCID, + value.SqlCompareOptions, + userDefinedType: null + ); setters.SetVariantMetaData(sink, ordinal, metaData); sink.ProcessMessagesAndThrow(); } @@ -3911,23 +3700,25 @@ private static void SetSqlXml_Unchecked(SmiEventSink_Default sink, ITypedSetters private static void SetXmlReader_Unchecked(SmiEventSink_Default sink, ITypedSettersV3 setters, int ordinal, XmlReader xmlReader) { // set up writer - XmlWriterSettings WriterSettings = new(); - WriterSettings.CloseOutput = false; // don't close the memory stream - WriterSettings.ConformanceLevel = ConformanceLevel.Fragment; - WriterSettings.Encoding = System.Text.Encoding.Unicode; - WriterSettings.OmitXmlDeclaration = true; - - System.IO.Stream target = new SmiSettersStream(sink, setters, ordinal, SmiMetaData.DefaultXml); - - XmlWriter xmlWriter = XmlWriter.Create(target, WriterSettings); + XmlWriterSettings WriterSettings = new XmlWriterSettings + { + CloseOutput = false, // don't close the memory stream + ConformanceLevel = ConformanceLevel.Fragment, + Encoding = System.Text.Encoding.Unicode, + OmitXmlDeclaration = true + }; - // now spool the data into the writer (WriteNode will call Read()) - xmlReader.Read(); - while (!xmlReader.EOF) + using (Stream target = new SmiSettersStream(sink, setters, ordinal, SmiMetaData.DefaultXml)) + using (XmlWriter xmlWriter = XmlWriter.Create(target, WriterSettings)) { - xmlWriter.WriteNode(xmlReader, true); + // now spool the data into the writer (WriteNode will call Read()) + xmlReader.Read(); + while (!xmlReader.EOF) + { + xmlWriter.WriteNode(xmlReader, true); + } + xmlWriter.Flush(); } - xmlWriter.Flush(); sink.ProcessMessagesAndThrow(); } @@ -3945,7 +3736,7 @@ private static void SetDbDataReader_Unchecked( int ordinal, SmiMetaData metaData, DbDataReader value - ) + ) { // Get the target gettersetter setters = setters.GetTypedGetterSetter(sink, ordinal); @@ -3971,7 +3762,7 @@ private static void SetIEnumerableOfSqlDataRecord_Unchecked( SmiMetaData metaData, IEnumerable value, ParameterPeekAheadValue peekAhead - ) + ) { // Get target gettersetter setters = setters.GetTypedGetterSetter(sink, ordinal); @@ -4005,39 +3796,36 @@ ParameterPeekAheadValue peekAhead enumerator = value.GetEnumerator(); } - using (enumerator) + while (enumerator.MoveNext()) { - while (enumerator.MoveNext()) - { - setters.NewElement(sink); - sink.ProcessMessagesAndThrow(); + setters.NewElement(sink); + sink.ProcessMessagesAndThrow(); - SqlDataRecord record = enumerator.Current; + SqlDataRecord record = enumerator.Current; - if (record.FieldCount != mdFields.Length) - { - throw SQL.EnumeratedRecordFieldCountChanged(recordNumber); - } + if (record.FieldCount != mdFields.Length) + { + throw SQL.EnumeratedRecordFieldCountChanged(recordNumber); + } - for (int i = 0; i < record.FieldCount; i++) + for (int i = 0; i < record.FieldCount; i++) + { + if (!MetaDataUtilsSmi.IsCompatible(metaData.FieldMetaData[i], record.GetSqlMetaData(i))) { - if (!MetaDataUtilsSmi.IsCompatible(metaData.FieldMetaData[i], record.GetSqlMetaData(i))) - { - throw SQL.EnumeratedRecordMetaDataChanged(record.GetName(i), recordNumber); - } + throw SQL.EnumeratedRecordMetaDataChanged(record.GetName(i), recordNumber); } - - FillCompatibleSettersFromRecord(sink, setters, mdFields, record, defaults); - recordNumber++; } - setters.EndElements(sink); - sink.ProcessMessagesAndThrow(); + FillCompatibleSettersFromRecord(sink, setters, mdFields, record, defaults); + recordNumber++; } + + setters.EndElements(sink); + sink.ProcessMessagesAndThrow(); + } finally { - // Clean up! if (enumerator is IDisposable disposable) { disposable.Dispose(); @@ -4051,7 +3839,7 @@ private static void SetDataTable_Unchecked( int ordinal, SmiMetaData metaData, DataTable value - ) + ) { // Get the target gettersetter setters = setters.GetTypedGetterSetter(sink, ordinal); @@ -4093,7 +3881,7 @@ DataTable value #endif ); } - SetCompatibleValueV200(sink, setters, i, fieldMetaData, cellValue, cellTypes[i], 0, NoLengthLimit, null); + SetCompatibleValueV200(sink, setters, i, fieldMetaData, cellValue, cellTypes[i], 0, null); } } } @@ -4103,34 +3891,28 @@ DataTable value } // spool a Stream into a scratch stream from the Smi interface and return it as a Stream - internal static Stream CopyIntoNewSmiScratchStream(Stream source, SmiEventSink_Default sink -#if NETFRAMEWORK - , SmiContext context -#endif - ) + internal static Stream CopyIntoNewSmiScratchStream(Stream source, SmiEventSink_Default sink, SmiContext context) { + Stream dest = null; #if NETFRAMEWORK - Stream dest; - if (null == context) - { - dest = new MemoryStream(); - } - else + if (context != null) { dest = new SqlClientWrapperSmiStream(sink, context.GetScratchStream(sink)); } -#else - Stream dest = new MemoryStream(); #endif + if (dest == null) + { + dest = new MemoryStream(); + } int chunkSize; - if (source.CanSeek && __maxByteChunkSize > source.Length) + if (source.CanSeek && source.Length > MaxByteChunkSize) { chunkSize = unchecked((int)source.Length); // unchecked cast is safe due to check on line above } else { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } byte[] copyBuffer = new byte[chunkSize]; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs index 86d83dd45d..53bcedf146 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; using System.Data; -using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Reflection; -using System.Xml; using Microsoft.Data.Common; using Microsoft.Data.SqlTypes; @@ -123,7 +119,7 @@ internal static TimeSpan GetTimeSpan(SmiEventSink_Default sink, ITypedGettersV3 return GetTimeSpan(sink, (SmiTypedGetterSetter)getters, ordinal, metaData); } ThrowIfITypedGettersIsNull(sink, getters, ordinal); - object obj = GetValue(sink, getters, ordinal, metaData, null); + object obj = GetValue(sink, getters, ordinal, metaData); if (null == obj) { throw ADP.InvalidCast(); @@ -176,7 +172,7 @@ internal static object GetOutputParameterV3Smi( SmiMetaData metaData, // Getter's type for this ordinal SmiContext context, // used to obtain scratch streams SqlBuffer targetBuffer // destination - ) + ) { object result = null; // Workaround for UDT hack in non-Smi code paths. if (IsDBNull_Unchecked(sink, getters, ordinal)) @@ -273,7 +269,7 @@ internal static object GetOutputParameterV200Smi( SmiMetaData metaData, // Getter's type for this ordinal SmiContext context, // used to obtain scratch streams SqlBuffer targetBuffer // destination - ) + ) { object result = null; // Workaround for UDT hack in non-Smi code paths. if (IsDBNull_Unchecked(sink, getters, ordinal)) @@ -485,13 +481,13 @@ internal static SqlStreamChars CopyIntoNewSmiScratchStreamChars(Stream source, S SqlClientWrapperSmiStreamChars dest = new(sink, context.GetScratchStream(sink)); int chunkSize; - if (source.CanSeek && __maxByteChunkSize > source.Length) + if (source.CanSeek && source.Length < MaxByteChunkSize) { chunkSize = unchecked((int)source.Length); // unchecked cast is safe due to check on line above } else { - chunkSize = __maxByteChunkSize; + chunkSize = MaxByteChunkSize; } byte[] copyBuffer = new byte[chunkSize]; @@ -508,6 +504,5 @@ internal static SqlStreamChars CopyIntoNewSmiScratchStreamChars(Stream source, S return dest; } - } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs similarity index 99% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs index 77c954b88f..56ae335e46 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs @@ -624,6 +624,10 @@ internal SqlBinary SqlBinary { if (StorageType.SqlBinary == _type) { + if (IsNull) + { + return SqlBinary.Null; + } return (SqlBinary)_object; } return (SqlBinary)SqlValue; // anything else we haven't thought of goes through boxing. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs index c897b70a6d..3690975f73 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientEventSource.cs @@ -800,6 +800,15 @@ internal void TryAdvancedTraceEvent(string messa } } + [NonEvent] + internal void TryAdvancedTraceEvent(string message, T0 args0, T1 args1, T2 args2, T3 args3, T4 args4, T5 args5, T6 args6) + { + if (Log.IsAdvancedTraceOn()) + { + AdvancedTrace(string.Format(message, args0?.ToString() ?? NullStr, args1?.ToString() ?? NullStr, args2?.ToString() ?? NullStr, args3?.ToString() ?? NullStr, args4?.ToString() ?? NullStr, args5?.ToString() ?? NullStr, args6?.ToString() ?? NullStr)); + } + } + [NonEvent] internal long TryAdvancedScopeEnterEvent(string message, T0 args0) { @@ -975,9 +984,9 @@ internal long TrySNIScopeEnterEvent(string className, [System.Runtime.CompilerSe { if (Log.IsSNIScopeEnabled()) { - StringBuilder sb = new StringBuilder(className); - sb.Append(".").Append(memberName).Append(" | SNI | INFO | SCOPE | Entering Scope {0}"); - return SNIScopeEnter(sb.ToString()); + long scopeId = Interlocked.Increment(ref s_nextSNIScopeId); + WriteEvent(SNIScopeEnterId, $"{className}.{memberName} | SNI | INFO | SCOPE | Entering Scope {scopeId}"); + return scopeId; } return 0; } @@ -1008,7 +1017,6 @@ internal void BeginExecute(int objectId, string dataSource, string database, str [Event(EndExecuteEventId, Keywords = Keywords.ExecutionTrace, Task = Tasks.ExecuteCommand, Opcode = EventOpcode.Stop)] internal void EndExecute(int objectId, int compositestate, int sqlExceptionNumber, string message) { - WriteEvent(EndExecuteEventId, objectId, compositestate, sqlExceptionNumber, message); } @@ -1121,10 +1129,17 @@ internal static class EventType private readonly long _scopeId; public TrySNIEventScope(long scopeID) => _scopeId = scopeID; - public void Dispose() => - SqlClientEventSource.Log.SNIScopeLeave(string.Format("Exit SNI Scope {0}", _scopeId)); + public void Dispose() + { + if (_scopeId == 0) + { + return; + } + SqlClientEventSource.Log.TrySNIScopeLeaveEvent(_scopeId); + } - public static TrySNIEventScope Create(string message) => new TrySNIEventScope(SqlClientEventSource.Log.SNIScopeEnter(message)); + public static TrySNIEventScope Create(string className, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") + => new TrySNIEventScope(SqlClientEventSource.Log.TrySNIScopeEnterEvent(className, memberName)); } internal readonly ref struct TryEventScope //: IDisposable diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs index 04ac2aab0f..d67d58d18d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlClientMetaDataCollectionNames.cs @@ -48,5 +48,9 @@ public static class SqlClientMetaDataCollectionNames /// public static readonly string ColumnSetColumns = "ColumnSetColumns"; + + /// + public static readonly string StructuredTypeMembers = "StructuredTypeMembers"; + } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs index adeb6cbb0b..c1cc23b4ae 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommandSet.cs @@ -16,7 +16,7 @@ namespace Microsoft.Data.SqlClient internal sealed class SqlCommandSet { private const string SqlIdentifierPattern = "^@[\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}_@#][\\p{Lo}\\p{Lu}\\p{Ll}\\p{Lm}\\p{Nd}\uff3f_@#\\$]*$"; - private static readonly Regex s_sqlIdentifierParser = new(SqlIdentifierPattern, RegexOptions.ExplicitCapture | RegexOptions.Singleline); + private static readonly Regex s_sqlIdentifierParser = new(SqlIdentifierPattern, RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.Compiled); private List _commandList = new(); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs new file mode 100644 index 0000000000..fde49b3980 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionEncryptOption.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Data.Common; + +namespace Microsoft.Data.SqlClient +{ + /// + public sealed class SqlConnectionEncryptOption + { + private const string TRUE = "True"; + private const string FALSE = "False"; + private const string STRICT = "Strict"; + private const string TRUE_LOWER = "true"; + private const string YES_LOWER = "yes"; + private const string MANDATORY_LOWER = "mandatory"; + private const string FALSE_LOWER = "false"; + private const string NO_LOWER = "no"; + private const string OPTIONAL_LOWER = "optional"; + private const string STRICT_LOWER = "strict"; + private readonly string _value; + private static readonly SqlConnectionEncryptOption s_optional = new(FALSE); + private static readonly SqlConnectionEncryptOption s_mandatory = new(TRUE); + private static readonly SqlConnectionEncryptOption s_strict = new(STRICT); + + private SqlConnectionEncryptOption(string value) + { + _value = value; + } + + /// + public static SqlConnectionEncryptOption Parse(string value) + { + if (TryParse(value, out SqlConnectionEncryptOption result)) + { + return result; + } + else + { + throw ADP.InvalidConnectionOptionValue(SqlConnectionString.KEY.Encrypt); + } + } + + /// + public static bool TryParse(string value, out SqlConnectionEncryptOption result) + { + switch (value?.ToLower()) + { + case TRUE_LOWER: + case YES_LOWER: + case MANDATORY_LOWER: + { + result = Mandatory; + return true; + } + case FALSE_LOWER: + case NO_LOWER: + case OPTIONAL_LOWER: + { + result = Optional; + return true; + } + case STRICT_LOWER: + { + result = Strict; + return true; + } + default: + result = null; + return false; + } + } + + /// + public static SqlConnectionEncryptOption Optional => s_optional; + + /// + public static SqlConnectionEncryptOption Mandatory => s_mandatory; + + /// + public static SqlConnectionEncryptOption Strict => s_strict; + + /// + public static implicit operator SqlConnectionEncryptOption(bool value) => value ? SqlConnectionEncryptOption.Mandatory : SqlConnectionEncryptOption.Optional; + + /// + public static implicit operator bool(SqlConnectionEncryptOption value) => !Optional.Equals(value); + + /// + public override string ToString() => _value; + + /// + public override bool Equals(object obj) + { + if (obj != null && + obj is SqlConnectionEncryptOption option) + { + return ToString().Equals(option.ToString()); + } + + return false; + } + + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + } + +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs index 310aaee04d..238885b368 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionPoolGroupProviderInfo.cs @@ -104,7 +104,8 @@ private PermissionSet CreateFailoverPermission(SqlConnectionString userConnectio // the server, we will use that name over what was specified // in the original connection string. - if (null == userConnectionOptions[SqlConnectionString.KEY.FailoverPartner]) + if (userConnectionOptions.ContainsKey(SqlConnectionString.KEY.FailoverPartner) && + null == userConnectionOptions[SqlConnectionString.KEY.FailoverPartner]) { keywordToReplace = SqlConnectionString.KEY.Data_Source; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs index f1a488c4b6..dd68546330 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionString.cs @@ -32,7 +32,8 @@ internal static class DEFAULT internal const int Connect_Timeout = DbConnectionStringDefaults.ConnectTimeout; internal const string Current_Language = DbConnectionStringDefaults.CurrentLanguage; internal const string Data_Source = DbConnectionStringDefaults.DataSource; - internal const bool Encrypt = DbConnectionStringDefaults.Encrypt; + internal static readonly SqlConnectionEncryptOption Encrypt = DbConnectionStringDefaults.Encrypt; + internal const string HostNameInCertificate = DbConnectionStringDefaults.HostNameInCertificate; internal const bool Enlist = DbConnectionStringDefaults.Enlist; internal const string FailoverPartner = DbConnectionStringDefaults.FailoverPartner; internal const string Initial_Catalog = DbConnectionStringDefaults.InitialCatalog; @@ -59,6 +60,8 @@ internal static class DEFAULT internal static readonly SqlAuthenticationMethod Authentication = DbConnectionStringDefaults.Authentication; internal static readonly SqlConnectionAttestationProtocol AttestationProtocol = DbConnectionStringDefaults.AttestationProtocol; internal static readonly SqlConnectionIPAddressPreference IpAddressPreference = DbConnectionStringDefaults.IPAddressPreference; + internal const string ServerSPN = DbConnectionStringDefaults.ServerSPN; + internal const string FailoverPartnerSPN = DbConnectionStringDefaults.FailoverPartnerSPN; #if NETFRAMEWORK internal static readonly bool TransparentNetworkIPResolution = DbConnectionStringDefaults.TransparentNetworkIPResolution; internal const bool Connection_Reset = DbConnectionStringDefaults.ConnectionReset; @@ -89,6 +92,7 @@ internal static class KEY internal const string Current_Language = DbConnectionStringKeywords.CurrentLanguage; internal const string Data_Source = DbConnectionStringKeywords.DataSource; internal const string Encrypt = DbConnectionStringKeywords.Encrypt; + internal const string HostNameInCertificate = DbConnectionStringKeywords.HostNameInCertificate; internal const string Enlist = DbConnectionStringKeywords.Enlist; internal const string FailoverPartner = DbConnectionStringKeywords.FailoverPartner; internal const string Initial_Catalog = DbConnectionStringKeywords.InitialCatalog; @@ -113,6 +117,8 @@ internal static class KEY internal const string Connect_Retry_Count = DbConnectionStringKeywords.ConnectRetryCount; internal const string Connect_Retry_Interval = DbConnectionStringKeywords.ConnectRetryInterval; internal const string Authentication = DbConnectionStringKeywords.Authentication; + internal const string Server_SPN = DbConnectionStringKeywords.ServerSPN; + internal const string Failover_Partner_SPN = DbConnectionStringKeywords.FailoverPartnerSPN; #if NETFRAMEWORK internal const string TransparentNetworkIPResolution = DbConnectionStringKeywords.TransparentNetworkIPResolution; #if ADONET_CERT_AUTH @@ -143,6 +149,8 @@ private static class SYNONYM internal const string ADDRESS = DbConnectionStringSynonyms.ADDRESS; internal const string SERVER = DbConnectionStringSynonyms.SERVER; internal const string NETWORK_ADDRESS = DbConnectionStringSynonyms.NETWORKADDRESS; + // host name in certificate + internal const string HOSTNAMEINCERTIFICATE = DbConnectionStringSynonyms.HOSTNAMEINCERTIFICATE; // initial catalog internal const string DATABASE = DbConnectionStringSynonyms.DATABASE; // integrated security @@ -173,6 +181,9 @@ private static class SYNONYM internal const string User = DbConnectionStringSynonyms.User; // workstation id internal const string WSID = DbConnectionStringSynonyms.WSID; + // server SPNs + internal const string ServerSPN = DbConnectionStringSynonyms.ServerSPN; + internal const string FailoverPartnerSPN = DbConnectionStringSynonyms.FailoverPartnerSPN; #if NETFRAMEWORK internal const string TRANSPARENTNETWORKIPRESOLUTION = DbConnectionStringSynonyms.TRANSPARENTNETWORKIPRESOLUTION; @@ -212,9 +223,9 @@ internal static class TRANSACTIONBINDING } #if NETFRAMEWORK - internal const int SynonymCount = 29; + internal const int SynonymCount = 32; #else - internal const int SynonymCount = 26; + internal const int SynonymCount = 29; internal const int DeprecatedSynonymCount = 2; #endif // NETFRAMEWORK @@ -222,7 +233,7 @@ internal static class TRANSACTIONBINDING private readonly bool _integratedSecurity; - private readonly bool _encrypt; + private readonly SqlConnectionEncryptOption _encrypt; private readonly bool _trustServerCertificate; private readonly bool _enlist; private readonly bool _mars; @@ -257,6 +268,9 @@ internal static class TRANSACTIONBINDING private readonly string _initialCatalog; private readonly string _password; private readonly string _userID; + private readonly string _hostNameInCertificate; + private readonly string _serverSPN; + private readonly string _failoverPartnerSPN; private readonly string _workstationId; @@ -289,7 +303,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _integratedSecurity = ConvertValueToIntegratedSecurity(); _poolBlockingPeriod = ConvertValueToPoolBlockingPeriod(); - _encrypt = ConvertValueToBoolean(KEY.Encrypt, DEFAULT.Encrypt); + _encrypt = ConvertValueToSqlConnectionEncrypt(); _enlist = ConvertValueToBoolean(KEY.Enlist, DEFAULT.Enlist); _mars = ConvertValueToBoolean(KEY.MARS, DEFAULT.MARS); _persistSecurityInfo = ConvertValueToBoolean(KEY.Persist_Security_Info, DEFAULT.Persist_Security_Info); @@ -322,6 +336,9 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G _enclaveAttestationUrl = ConvertValueToString(KEY.EnclaveAttestationUrl, DEFAULT.EnclaveAttestationUrl); _attestationProtocol = ConvertValueToAttestationProtocol(); _ipAddressPreference = ConvertValueToIPAddressPreference(); + _hostNameInCertificate = ConvertValueToString(KEY.HostNameInCertificate, DEFAULT.HostNameInCertificate); + _serverSPN = ConvertValueToString(KEY.Server_SPN, DEFAULT.ServerSPN); + _failoverPartnerSPN = ConvertValueToString(KEY.Failover_Partner_SPN, DEFAULT.FailoverPartnerSPN); // Temporary string - this value is stored internally as an enum. string typeSystemVersionString = ConvertValueToString(KEY.Type_System_Version, null); @@ -368,7 +385,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G // SQLPT 41700: Ignore ResetConnection=False (still validate the keyword/value) _connectionReset = ConvertValueToBoolean(KEY.Connection_Reset, DEFAULT.Connection_Reset); _contextConnection = ConvertValueToBoolean(KEY.Context_Connection, DEFAULT.Context_Connection); - _encrypt = ConvertValueToEncrypt(); + _encrypt = ConvertValueToSqlConnectionEncrypt(); _enlist = ConvertValueToBoolean(KEY.Enlist, ADP.s_isWindowsNT); _transparentNetworkIPResolution = ConvertValueToBoolean(KEY.TransparentNetworkIPResolution, DEFAULT.TransparentNetworkIPResolution); _networkLibrary = ConvertValueToString(KEY.Network_Library, null); @@ -400,7 +417,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G } } - if (!_encrypt) + if (_encrypt == SqlConnectionEncryptOption.Optional) { // Support legacy registry encryption settings const string folder = "Software\\Microsoft\\MSSQLServer\\Client\\SuperSocketNetLib"; const string value = "Encrypt"; @@ -408,7 +425,7 @@ internal SqlConnectionString(string connectionString) : base(connectionString, G object obj = ADP.LocalMachineRegistryValue(folder, value); if ((obj is int iObj) && (iObj == 1)) { // If the registry key exists - _encrypt = true; + _encrypt = SqlConnectionEncryptOption.Mandatory; } } @@ -675,6 +692,8 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS _columnEncryptionSetting = connectionOptions._columnEncryptionSetting; _enclaveAttestationUrl = connectionOptions._enclaveAttestationUrl; _attestationProtocol = connectionOptions._attestationProtocol; + _serverSPN = connectionOptions._serverSPN; + _failoverPartnerSPN = connectionOptions._failoverPartnerSPN; #if NETFRAMEWORK _connectionReset = connectionOptions._connectionReset; _contextConnection = connectionOptions._contextConnection; @@ -696,7 +715,8 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS // SQLPT 41700: Ignore ResetConnection=False, always reset the connection for security internal bool ConnectionReset => true; // internal bool EnableUdtDownload => _enableUdtDownload;} } - internal bool Encrypt => _encrypt; + internal SqlConnectionEncryptOption Encrypt => _encrypt; + internal string HostNameInCertificate => _hostNameInCertificate; internal bool TrustServerCertificate => _trustServerCertificate; internal bool Enlist => _enlist; internal bool MARS => _mars; @@ -732,7 +752,8 @@ internal SqlConnectionString(SqlConnectionString connectionOptions, string dataS internal string UserID => _userID; internal string WorkstationId => _workstationId; internal PoolBlockingPeriod PoolBlockingPeriod => _poolBlockingPeriod; - + internal string ServerSPN => _serverSPN; + internal string FailoverPartnerSPN => _failoverPartnerSPN; internal TypeSystem TypeSystemVersion => _typeSystemVersion; internal Version TypeSystemAssemblyVersion => _typeSystemAssemblyVersion; @@ -817,6 +838,7 @@ internal static Dictionary GetParseSynonyms() { KEY.Encrypt, KEY.Encrypt }, { KEY.Enlist, KEY.Enlist }, { KEY.FailoverPartner, KEY.FailoverPartner }, + { KEY.HostNameInCertificate, KEY.HostNameInCertificate }, { KEY.Initial_Catalog, KEY.Initial_Catalog }, { KEY.Integrated_Security, KEY.Integrated_Security }, { KEY.Load_Balance_Timeout, KEY.Load_Balance_Timeout }, @@ -843,10 +865,13 @@ internal static Dictionary GetParseSynonyms() { KEY.Connect_Retry_Interval, KEY.Connect_Retry_Interval }, { KEY.Authentication, KEY.Authentication }, { KEY.IPAddressPreference, KEY.IPAddressPreference }, + { KEY.Server_SPN, KEY.Server_SPN }, + { KEY.Failover_Partner_SPN, KEY.Failover_Partner_SPN }, { SYNONYM.APP, KEY.Application_Name }, { SYNONYM.APPLICATIONINTENT, KEY.ApplicationIntent }, { SYNONYM.EXTENDED_PROPERTIES, KEY.AttachDBFilename }, + { SYNONYM.HOSTNAMEINCERTIFICATE, KEY.HostNameInCertificate }, { SYNONYM.INITIAL_FILE_NAME, KEY.AttachDBFilename }, { SYNONYM.CONNECTRETRYCOUNT, KEY.Connect_Retry_Count }, { SYNONYM.CONNECTRETRYINTERVAL, KEY.Connect_Retry_Interval }, @@ -871,6 +896,8 @@ internal static Dictionary GetParseSynonyms() { SYNONYM.UID, KEY.User_ID }, { SYNONYM.User, KEY.User_ID }, { SYNONYM.WSID, KEY.Workstation_Id }, + { SYNONYM.ServerSPN, KEY.Server_SPN }, + { SYNONYM.FailoverPartnerSPN, KEY.Failover_Partner_SPN }, #if NETFRAMEWORK #if ADONET_CERT_AUTH { KEY.Certificate, KEY.Certificate }, @@ -1102,18 +1129,25 @@ internal PoolBlockingPeriod ConvertValueToPoolBlockingPeriod() } } -#if NETFRAMEWORK - protected internal override PermissionSet CreatePermissionSet() + internal SqlConnectionEncryptOption ConvertValueToSqlConnectionEncrypt() { - PermissionSet permissionSet = new(PermissionState.None); - permissionSet.AddPermission(new SqlClientPermission(this)); - return permissionSet; - } + if (!TryGetParsetableValue(KEY.Encrypt, out string value)) + { + return DEFAULT.Encrypt; + } - internal bool ConvertValueToEncrypt() - { - bool defaultEncryptValue = !Parsetable.ContainsKey(KEY.Authentication) ? DEFAULT.Encrypt : true; - return ConvertValueToBoolean(KEY.Encrypt, defaultEncryptValue); + try + { + return DbConnectionStringBuilderUtil.ConvertToSqlConnectionEncryptOption(KEY.Encrypt, value); + } + catch (FormatException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.Encrypt, e); + } + catch (OverflowException e) + { + throw ADP.InvalidConnectionOptionValue(KEY.Encrypt, e); + } } static internal Hashtable NetlibMapping() @@ -1166,6 +1200,21 @@ internal static class NETLIB } private static Hashtable s_netlibMapping; + +#if NETFRAMEWORK + protected internal override PermissionSet CreatePermissionSet() + { + PermissionSet permissionSet = new(PermissionState.None); + permissionSet.AddPermission(new SqlClientPermission(this)); + return permissionSet; + } + + internal SqlConnectionEncryptOption ConvertValueToEncrypt() + { + SqlConnectionEncryptOption defaultEncryptValue = !Parsetable.ContainsKey(KEY.Authentication) ? DEFAULT.Encrypt : SqlConnectionEncryptOption.Mandatory; + return ConvertValueToSqlConnectionEncrypt(); + } + private readonly bool _connectionReset; private readonly bool _contextConnection; private readonly bool _transparentNetworkIPResolution; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs index b78c2e392b..28a8fbc6fe 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionStringBuilder.cs @@ -46,6 +46,7 @@ private enum Keywords Replication, ConnectTimeout, Encrypt, + HostNameInCertificate, TrustServerCertificate, LoadBalanceTimeout, PacketSize, @@ -65,6 +66,8 @@ private enum Keywords AttestationProtocol, CommandTimeout, IPAddressPreference, + ServerSPN, + FailoverPartnerSPN, #if NETFRAMEWORK ConnectionReset, NetworkLibrary, @@ -105,7 +108,8 @@ private enum Keywords private int _packetSize = DbConnectionStringDefaults.PacketSize; private int _connectRetryCount = DbConnectionStringDefaults.ConnectRetryCount; private int _connectRetryInterval = DbConnectionStringDefaults.ConnectRetryInterval; - private bool _encrypt = DbConnectionStringDefaults.Encrypt; + private SqlConnectionEncryptOption _encrypt = DbConnectionStringDefaults.Encrypt; + private string _hostNameInCertificate = DbConnectionStringDefaults.HostNameInCertificate; private bool _trustServerCertificate = DbConnectionStringDefaults.TrustServerCertificate; private bool _enlist = DbConnectionStringDefaults.Enlist; private bool _integratedSecurity = DbConnectionStringDefaults.IntegratedSecurity; @@ -122,6 +126,8 @@ private enum Keywords private string _enclaveAttestationUrl = DbConnectionStringDefaults.EnclaveAttestationUrl; private SqlConnectionAttestationProtocol _attestationProtocol = DbConnectionStringDefaults.AttestationProtocol; private SqlConnectionIPAddressPreference _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; + private string _serverSPN = DbConnectionStringDefaults.ServerSPN; + private string _failoverPartnerSPN = DbConnectionStringDefaults.FailoverPartnerSPN; #if NETFRAMEWORK private bool _connectionReset = DbConnectionStringDefaults.ConnectionReset; @@ -149,6 +155,7 @@ private static string[] CreateValidKeywords() validKeywords[(int)Keywords.CurrentLanguage] = DbConnectionStringKeywords.CurrentLanguage; validKeywords[(int)Keywords.DataSource] = DbConnectionStringKeywords.DataSource; validKeywords[(int)Keywords.Encrypt] = DbConnectionStringKeywords.Encrypt; + validKeywords[(int)Keywords.HostNameInCertificate] = DbConnectionStringKeywords.HostNameInCertificate; validKeywords[(int)Keywords.Enlist] = DbConnectionStringKeywords.Enlist; validKeywords[(int)Keywords.FailoverPartner] = DbConnectionStringKeywords.FailoverPartner; validKeywords[(int)Keywords.InitialCatalog] = DbConnectionStringKeywords.InitialCatalog; @@ -176,11 +183,13 @@ private static string[] CreateValidKeywords() validKeywords[(int)Keywords.EnclaveAttestationUrl] = DbConnectionStringKeywords.EnclaveAttestationUrl; validKeywords[(int)Keywords.AttestationProtocol] = DbConnectionStringKeywords.AttestationProtocol; validKeywords[(int)Keywords.IPAddressPreference] = DbConnectionStringKeywords.IPAddressPreference; + validKeywords[(int)Keywords.ServerSPN] = DbConnectionStringKeywords.ServerSPN; + validKeywords[(int)Keywords.FailoverPartnerSPN] = DbConnectionStringKeywords.FailoverPartnerSPN; #if NETFRAMEWORK validKeywords[(int)Keywords.ConnectionReset] = DbConnectionStringKeywords.ConnectionReset; + validKeywords[(int)Keywords.NetworkLibrary] = DbConnectionStringKeywords.NetworkLibrary; validKeywords[(int)Keywords.ContextConnection] = DbConnectionStringKeywords.ContextConnection; validKeywords[(int)Keywords.TransparentNetworkIPResolution] = DbConnectionStringKeywords.TransparentNetworkIPResolution; - validKeywords[(int)Keywords.NetworkLibrary] = DbConnectionStringKeywords.NetworkLibrary; #if ADONET_CERT_AUTH validKeywords[(int)Keywords.Certificate] = DbConnectionStringKeywords.Certificate; #endif @@ -203,6 +212,7 @@ private static Dictionary CreateKeywordsDictionary() { DbConnectionStringKeywords.Encrypt, Keywords.Encrypt }, { DbConnectionStringKeywords.Enlist, Keywords.Enlist }, { DbConnectionStringKeywords.FailoverPartner, Keywords.FailoverPartner }, + { DbConnectionStringKeywords.HostNameInCertificate, Keywords.HostNameInCertificate }, { DbConnectionStringKeywords.InitialCatalog, Keywords.InitialCatalog }, { DbConnectionStringKeywords.IntegratedSecurity, Keywords.IntegratedSecurity }, { DbConnectionStringKeywords.LoadBalanceTimeout, Keywords.LoadBalanceTimeout }, @@ -228,6 +238,8 @@ private static Dictionary CreateKeywordsDictionary() { DbConnectionStringKeywords.EnclaveAttestationUrl, Keywords.EnclaveAttestationUrl }, { DbConnectionStringKeywords.AttestationProtocol, Keywords.AttestationProtocol }, { DbConnectionStringKeywords.IPAddressPreference, Keywords.IPAddressPreference }, + { DbConnectionStringKeywords.ServerSPN, Keywords.ServerSPN }, + { DbConnectionStringKeywords.FailoverPartnerSPN, Keywords.FailoverPartnerSPN }, #if NETFRAMEWORK { DbConnectionStringKeywords.ConnectionReset, Keywords.ConnectionReset }, @@ -245,6 +257,7 @@ private static Dictionary CreateKeywordsDictionary() { DbConnectionStringSynonyms.APP, Keywords.ApplicationName }, { DbConnectionStringSynonyms.APPLICATIONINTENT, Keywords.ApplicationIntent }, { DbConnectionStringSynonyms.EXTENDEDPROPERTIES, Keywords.AttachDBFilename }, + { DbConnectionStringSynonyms.HOSTNAMEINCERTIFICATE, Keywords.HostNameInCertificate }, { DbConnectionStringSynonyms.INITIALFILENAME, Keywords.AttachDBFilename }, { DbConnectionStringSynonyms.CONNECTIONTIMEOUT, Keywords.ConnectTimeout }, { DbConnectionStringSynonyms.CONNECTRETRYCOUNT, Keywords.ConnectRetryCount }, @@ -266,7 +279,9 @@ private static Dictionary CreateKeywordsDictionary() { DbConnectionStringSynonyms.PERSISTSECURITYINFO, Keywords.PersistSecurityInfo }, { DbConnectionStringSynonyms.UID, Keywords.UserID }, { DbConnectionStringSynonyms.User, Keywords.UserID }, - { DbConnectionStringSynonyms.WSID, Keywords.WorkstationID } + { DbConnectionStringSynonyms.WSID, Keywords.WorkstationID }, + { DbConnectionStringSynonyms.ServerSPN, Keywords.ServerSPN }, + { DbConnectionStringSynonyms.FailoverPartnerSPN, Keywords.FailoverPartnerSPN }, }; Debug.Assert((KeywordsCount + SqlConnectionString.SynonymCount) == pairs.Count, "initial expected size is incorrect"); return pairs; @@ -290,6 +305,10 @@ private static SqlConnectionColumnEncryptionSetting ConvertToColumnEncryptionSet private static SqlConnectionAttestationProtocol ConvertToAttestationProtocol(string keyword, object value) => DbConnectionStringBuilderUtil.ConvertToAttestationProtocol(keyword, value); + private static SqlConnectionEncryptOption ConvertToSqlConnectionEncryptOption(string keyword, object value) + => DbConnectionStringBuilderUtil.ConvertToSqlConnectionEncryptOption(keyword, value); + + private static SqlConnectionIPAddressPreference ConvertToIPAddressPreference(string keyword, object value) => DbConnectionStringBuilderUtil.ConvertToIPAddressPreference(keyword, value); @@ -318,6 +337,8 @@ private object GetAt(Keywords index) return DataSource; case Keywords.Encrypt: return Encrypt; + case Keywords.HostNameInCertificate: + return HostNameInCertificate; case Keywords.Enlist: return Enlist; case Keywords.FailoverPartner: @@ -373,7 +394,10 @@ private object GetAt(Keywords index) return AttestationProtocol; case Keywords.IPAddressPreference: return IPAddressPreference; - + case Keywords.ServerSPN: + return ServerSPN; + case Keywords.FailoverPartnerSPN: + return FailoverPartnerSPN; #if NETFRAMEWORK #pragma warning disable 618 // Obsolete properties case Keywords.ConnectionReset: @@ -440,6 +464,9 @@ private void Reset(Keywords index) case Keywords.Encrypt: _encrypt = DbConnectionStringDefaults.Encrypt; break; + case Keywords.HostNameInCertificate: + _hostNameInCertificate = DbConnectionStringDefaults.HostNameInCertificate; + break; case Keywords.Enlist: _enlist = DbConnectionStringDefaults.Enlist; break; @@ -518,6 +545,12 @@ private void Reset(Keywords index) case Keywords.IPAddressPreference: _ipAddressPreference = DbConnectionStringDefaults.IPAddressPreference; break; + case Keywords.ServerSPN: + _serverSPN = DbConnectionStringDefaults.ServerSPN; + break; + case Keywords.FailoverPartnerSPN: + _failoverPartnerSPN = DbConnectionStringDefaults.FailoverPartnerSPN; + break; #if NETFRAMEWORK case Keywords.ConnectionReset: _connectionReset = DbConnectionStringDefaults.ConnectionReset; @@ -571,6 +604,11 @@ private void SetAttestationProtocolValue(SqlConnectionAttestationProtocol value) base[DbConnectionStringKeywords.AttestationProtocol] = DbConnectionStringBuilderUtil.AttestationProtocolToString(value); } + private void SetSqlConnectionEncryptionValue(SqlConnectionEncryptOption value) + { + base[DbConnectionStringKeywords.Encrypt] = value.ToString(); + } + private void SetIPAddressPreferenceValue(SqlConnectionIPAddressPreference value) { Debug.Assert(DbConnectionStringBuilderUtil.IsValidIPAddressPreference(value), "Invalid value for SqlConnectionIPAddressPreference"); @@ -978,7 +1016,10 @@ public override object this[string keyword] PoolBlockingPeriod = ConvertToPoolBlockingPeriod(keyword, value); break; case Keywords.Encrypt: - Encrypt = ConvertToBoolean(value); + Encrypt = ConvertToSqlConnectionEncryptOption(keyword, value); + break; + case Keywords.HostNameInCertificate: + HostNameInCertificate = ConvertToString(value); break; case Keywords.TrustServerCertificate: TrustServerCertificate = ConvertToBoolean(value); @@ -1010,6 +1051,12 @@ public override object this[string keyword] case Keywords.ConnectRetryInterval: ConnectRetryInterval = ConvertToInt32(value); break; + case Keywords.ServerSPN: + ServerSPN = ConvertToString(value); + break; + case Keywords.FailoverPartnerSPN: + FailoverPartnerSPN = ConvertToString(value); + break; #if NETFRAMEWORK #pragma warning disable 618 // Obsolete properties case Keywords.ConnectionReset: @@ -1165,21 +1212,51 @@ public string DataSource } } + /// + [DisplayName(DbConnectionStringKeywords.ServerSPN)] + [ResCategory(StringsHelper.ResourceNames.DataCategory_Source)] + [ResDescription(StringsHelper.ResourceNames.DbConnectionString_ServerSPN)] + [RefreshProperties(RefreshProperties.All)] + public string ServerSPN + { + get => _serverSPN; + set + { + SetValue(DbConnectionStringKeywords.ServerSPN, value); + _serverSPN = value; + } + } + /// [DisplayName(DbConnectionStringKeywords.Encrypt)] [ResCategory(StringsHelper.ResourceNames.DataCategory_Security)] [ResDescription(StringsHelper.ResourceNames.DbConnectionString_Encrypt)] [RefreshProperties(RefreshProperties.All)] - public bool Encrypt + public SqlConnectionEncryptOption Encrypt { get => _encrypt; set { - SetValue(DbConnectionStringKeywords.Encrypt, value); + SetSqlConnectionEncryptionValue(value); _encrypt = value; } } + /// + [DisplayName(DbConnectionStringKeywords.HostNameInCertificate)] + [ResCategory(StringsHelper.ResourceNames.DataCategory_Security)] + [ResDescription(StringsHelper.ResourceNames.DbConnectionString_Encrypt)] + [RefreshProperties(RefreshProperties.All)] + public string HostNameInCertificate + { + get => _hostNameInCertificate; + set + { + SetValue(DbConnectionStringKeywords.HostNameInCertificate, value); + _hostNameInCertificate = value; + } + } + /// [DisplayName(DbConnectionStringKeywords.ColumnEncryptionSetting)] [ResCategory(StringsHelper.ResourceNames.DataCategory_Security)] @@ -1303,6 +1380,21 @@ public string FailoverPartner } } + /// + [DisplayName(DbConnectionStringKeywords.FailoverPartnerSPN)] + [ResCategory(StringsHelper.ResourceNames.DataCategory_Source)] + [ResDescription(StringsHelper.ResourceNames.DbConnectionString_FailoverPartnerSPN)] + [RefreshProperties(RefreshProperties.All)] + public string FailoverPartnerSPN + { + get => _failoverPartnerSPN; + set + { + SetValue(DbConnectionStringKeywords.FailoverPartnerSPN, value); + _failoverPartnerSPN = value; + } + } + /// [DisplayName(DbConnectionStringKeywords.InitialCatalog)] [ResCategory(StringsHelper.ResourceNames.DataCategory_Source)] diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index ce266f7fb9..440755ddca 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -106,6 +106,7 @@ internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, str _con.Open(); _cachedServer = _con.DataSource; + bool? dbId = null; #if NETFRAMEWORK if (hashHelper.Identity != null) @@ -125,7 +126,16 @@ internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, str CommandText = "select is_broker_enabled from sys.databases where database_id=db_id()" }; - if (!(bool)_com.ExecuteScalar()) + // db_id() returns the database ID of the current database hence it will always be one line result + using (SqlDataReader reader = _com.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.Read() && reader[0] is not null) + { + dbId = reader.GetBoolean(0); + } + } + + if (dbId is null || !dbId.Value) { throw SQL.SqlDependencyDatabaseBrokerDisabled(); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AppDomain.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs similarity index 100% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.AssemblyLoadContext.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs similarity index 96% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs index 15f2b573e2..404e27d788 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDependencyUtils.cs @@ -18,15 +18,14 @@ internal partial class SqlDependencyPerAppDomainDispatcher : MarshalByRefObject { // Instance members - internal static readonly SqlDependencyPerAppDomainDispatcher - SingletonInstance = new SqlDependencyPerAppDomainDispatcher(); // singleton object + internal static readonly SqlDependencyPerAppDomainDispatcher SingletonInstance = new(); // singleton object - internal object _instanceLock = new object(); + internal object _instanceLock = new(); // Dependency ID -> Dependency hashtable. 1 -> 1 mapping. // 1) Used for ASP.NET to map from ID to dependency. // 2) Used to enumerate dependencies to invalidate based on server. - private Dictionary _dependencyIdToDependencyHash; + private readonly Dictionary _dependencyIdToDependencyHash; // holds dependencies list per notification and the command hash from which this notification was generated // command hash is needed to remove its entry from _commandHashToNotificationId when the notification is removed @@ -46,12 +45,12 @@ internal DependencyList(string commandHash) // resource effect on SQL Server. The Guid identifier is sent to the server during notification enlistment, // and returned during the notification event. Dependencies look up existing Guids, if one exists, to ensure // they are re-using notification ids. - private Dictionary _notificationIdToDependenciesHash; + private readonly Dictionary _notificationIdToDependenciesHash; // CommandHash value -> notificationId associated with it: 1->1 mapping. This map is used to quickly find if we need to create // new notification or hookup into existing one. // CommandHash is built from connection string, command text and parameters - private Dictionary _commandHashToNotificationId; + private readonly Dictionary _commandHashToNotificationId; // TIMEOUT LOGIC DESCRIPTION // @@ -73,10 +72,10 @@ internal DependencyList(string commandHash) private DateTime _nextTimeout; // Timer to periodically check the dependencies in the table and see if anyone needs // a timeout. We'll enable this only on demand. - private Timer _timeoutTimer; + private readonly Timer _timeoutTimer; - private static int _objectTypeCount; // EventSource counter - internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); + private static int s_objectTypeCount; // EventSource counter + internal int ObjectID { get; } = Interlocked.Increment(ref s_objectTypeCount); private SqlDependencyPerAppDomainDispatcher() { @@ -86,7 +85,12 @@ private SqlDependencyPerAppDomainDispatcher() _dependencyIdToDependencyHash = new Dictionary(); _notificationIdToDependenciesHash = new Dictionary(); _commandHashToNotificationId = new Dictionary(); +#if NETFRAMEWORK + _timeoutTimer = new Timer(new TimerCallback(TimeoutTimerCallback), null, Timeout.Infinite, Timeout.Infinite); + // If rude abort - we'll leak. This is acceptable for now. + AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadEventHandler); +#else _timeoutTimer = ADP.UnsafeCreateTimer( new TimerCallback(TimeoutTimerCallback), null, @@ -95,6 +99,7 @@ private SqlDependencyPerAppDomainDispatcher() SubscribeToAppDomainUnload(); SubscribeToAssemblyLoadContextUnload(); +#endif // NETFRAMEWORK } finally { @@ -102,10 +107,11 @@ private SqlDependencyPerAppDomainDispatcher() } } +#if NETCOREAPP || NETSTANDARD partial void SubscribeToAppDomainUnload(); partial void SubscribeToAssemblyLoadContextUnload(); - +#endif private void UnloadEventHandler(object sender, EventArgs e) { long scopeID = SqlClientEventSource.Log.TryNotificationScopeEnterEvent("SqlDependencyPerAppDomainDispatcher.UnloadEventHandler | DEP | Object Id {0}", ObjectID); @@ -115,7 +121,7 @@ private void UnloadEventHandler(object sender, EventArgs e) // stopping of all start calls in this AppDomain. For containers shared among various AppDomains, // this will just be a ref-count subtract. For non-shared containers, we will close the container // and clean-up. - var dispatcher = SqlDependency.ProcessDispatcher; + SqlDependencyProcessDispatcher dispatcher = SqlDependency.ProcessDispatcher; dispatcher?.QueueAppDomainUnloading(SqlDependency.AppDomainKey); } finally @@ -171,8 +177,7 @@ internal string AddCommandEntry(string commandHash, SqlDependency dep) { // we have one or more SqlDependency instances with same command hash - DependencyList dependencyList = null; - if (!_notificationIdToDependenciesHash.TryGetValue(notificationId, out dependencyList)) + if (!_notificationIdToDependenciesHash.TryGetValue(notificationId, out DependencyList dependencyList)) { // this should not happen since _commandHashToNotificationId and _notificationIdToDependenciesHash are always // updated together @@ -203,7 +208,7 @@ internal string AddCommandEntry(string commandHash, SqlDependency dep) Guid.NewGuid().ToString("D", System.Globalization.CultureInfo.InvariantCulture) ); SqlClientEventSource.Log.TryNotificationTraceEvent(" Creating new Dependencies list for commandHash."); - DependencyList dependencyList = new DependencyList(commandHash); + DependencyList dependencyList = new(commandHash); dependencyList.Add(dep); // map command hash to notification we just created to reuse it for the next client @@ -289,7 +294,7 @@ internal void InvalidateServer(string server, SqlNotification sqlNotification) long scopeID = SqlClientEventSource.Log.TryNotificationScopeEnterEvent(" {0}, server: '{1}'", ObjectID, server); try { - List dependencies = new List(); + List dependencies = new(); lock (_instanceLock) { // Copy inside of lock, but invalidate outside of lock. @@ -449,8 +454,8 @@ private void RemoveDependencyFromCommandToDependenciesHash(SqlDependency depende { lock (_instanceLock) { - List notificationIdsToRemove = new List(); - List commandHashesToRemove = new List(); + List notificationIdsToRemove = new(); + List commandHashesToRemove = new(); foreach (KeyValuePair entry in _notificationIdToDependenciesHash) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs index 06262b364f..c2e9739237 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnclaveAttestationParameters.Crypto.cs @@ -7,12 +7,12 @@ namespace Microsoft.Data.SqlClient { - /// + /// internal class SqlEnclaveAttestationParameters { private readonly byte[] _input; - /// + /// internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHellman clientDiffieHellmanKey) { if (input == null) @@ -29,13 +29,13 @@ internal SqlEnclaveAttestationParameters(int protocol, byte[] input, ECDiffieHel ClientDiffieHellmanKey = clientDiffieHellmanKey; } - /// + /// internal int Protocol { get; private set; } - /// + /// internal ECDiffieHellman ClientDiffieHellmanKey { get; private set; } - /// + /// internal byte[] GetInput() { // return a new array for safety so the caller cannot mutate the original diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs index 2037fe57c7..bb474a0465 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs @@ -15,7 +15,7 @@ using System.IO; using System.Xml; using Microsoft.Data.Common; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient { @@ -929,7 +929,7 @@ internal static string GetStringFromXml(XmlReader xmlreader) private static readonly MetaType s_metaTable = new(255, 255, -1, false, false, false, TdsEnums.SQLTABLE, TdsEnums.SQLTABLE, MetaTypeName.TABLE, typeof(IEnumerable), typeof(IEnumerable), SqlDbType.Structured, DbType.Object, 0); - private static readonly MetaType s_metaSUDT = new(255, 255, -1, false, false, false, TdsEnums.SQLVOID, TdsEnums.SQLVOID, "", typeof(SqlDataRecord), typeof(SqlDataRecord), SqlDbType.Structured, DbType.Object, 0); + private static readonly MetaType s_metaSUDT = new(255, 255, -1, false, false, false, TdsEnums.SQLVOID, TdsEnums.SQLVOID, "", typeof(Server.SqlDataRecord), typeof(Server.SqlDataRecord), SqlDbType.Structured, DbType.Object, 0); private static readonly MetaType s_metaDate = new(255, 255, 3, true, false, false, TdsEnums.SQLDATE, TdsEnums.SQLDATE, MetaTypeName.DATE, typeof(System.DateTime), typeof(System.DateTime), SqlDbType.Date, DbType.Date, 0); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs new file mode 100644 index 0000000000..f441892f29 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Buffers; + +namespace Microsoft.Data.SqlClient +{ + internal sealed class SqlEnvChange + { + internal byte _type; + internal byte _oldLength; + internal int _newLength; // 7206 TDS changes makes this length an int + internal int _length; + internal string _newValue; + internal string _oldValue; + /// + /// contains binary data, before using this field check newBinRented to see if you can take the field array or whether you should allocate and copy + /// + internal byte[] _newBinValue; + /// + /// contains binary data, before using this field check newBinRented to see if you can take the field array or whether you should allocate and copy + /// + internal byte[] _oldBinValue; + internal long _newLongValue; + internal long _oldLongValue; + internal SqlCollation _newCollation; + internal SqlCollation _oldCollation; + internal RoutingInfo _newRoutingInfo; + internal bool _newBinRented; + internal bool _oldBinRented; + + internal SqlEnvChange _next; + + internal void Clear() + { + _type = 0; + _oldLength = 0; + _newLength = 0; + _length = 0; + _newValue = null; + _oldValue = null; + if (_newBinValue != null) + { + Array.Clear(_newBinValue, 0, _newBinValue.Length); + if (_newBinRented) + { + ArrayPool.Shared.Return(_newBinValue); + } + _newBinValue = null; + } + if (_oldBinValue != null) + { + Array.Clear(_oldBinValue, 0, _oldBinValue.Length); + if (_oldBinRented) + { + ArrayPool.Shared.Return(_oldBinValue); + } + _oldBinValue = null; + } + _newBinRented = false; + _oldBinRented = false; + _newLongValue = 0; + _oldLongValue = 0; + _newCollation = null; + _oldCollation = null; + _newRoutingInfo = null; + _next = null; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlError.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlError.cs new file mode 100644 index 0000000000..2fc7dd605e --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlError.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.Data.SqlClient +{ + /// + [Serializable] + public sealed class SqlError + { + // bug fix - MDAC 48965 - missing source of exception + private readonly string _source = TdsEnums.SQL_PROVIDER_NAME; + private readonly int _number; + private readonly byte _state; + private readonly byte _errorClass; + [System.Runtime.Serialization.OptionalField(VersionAdded = 2)] + private readonly string _server; + private readonly string _message; + private readonly string _procedure; + private readonly int _lineNumber; + [System.Runtime.Serialization.OptionalField(VersionAdded = 4)] + private readonly int _win32ErrorCode; + [System.Runtime.Serialization.OptionalField(VersionAdded = 5)] + private readonly Exception _exception; + + internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, uint win32ErrorCode, Exception exception = null) + : this(infoNumber, errorState, errorClass, server, errorMessage, procedure, lineNumber, exception) + { + _server = server; + _win32ErrorCode = (int)win32ErrorCode; + } + + internal SqlError(int infoNumber, byte errorState, byte errorClass, string server, string errorMessage, string procedure, int lineNumber, Exception exception = null) + { + _number = infoNumber; + _state = errorState; + _errorClass = errorClass; + _server = server; + _message = errorMessage; + _procedure = procedure; + _lineNumber = lineNumber; + _win32ErrorCode = 0; + _exception = exception; + if (errorClass != 0) + { + SqlClientEventSource.Log.TryTraceEvent("SqlError.ctor | ERR | Info Number {0}, Error State {1}, Error Class {2}, Error Message '{3}', Procedure '{4}', Line Number {5}", infoNumber, (int)errorState, (int)errorClass, errorMessage, procedure ?? "None", (int)lineNumber); + } + } + + /// + // bug fix - MDAC #49280 - SqlError does not implement ToString(); + // There is no exception stack included because the correct exception stack is only available + // on SqlException, and to obtain that the SqlError would have to have backpointers all the + // way back to SqlException. If the user needs a call stack, they can obtain it on SqlException. + public override string ToString() + { + return typeof(SqlError).ToString() + ": " + Message; // since this is sealed so we can change GetType to typeof + } + + /// + // bug fix - MDAC #48965 - missing source of exception + public string Source => _source; + + /// + public int Number => _number; + + /// + public byte State => _state; + + /// + public byte Class => _errorClass; + + /// + public string Server => _server; + + /// + public string Message => _message; + + /// + public string Procedure => _procedure; + + /// + public int LineNumber => _lineNumber; + + internal int Win32ErrorCode => _win32ErrorCode; + + internal Exception Exception => _exception; + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs similarity index 80% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs index 5d706c6c0a..114363f5a6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs @@ -26,22 +26,22 @@ internal enum TransactionType Delegated = 3, Distributed = 4, Context = 5, // only valid in proc. - }; + } sealed internal class SqlInternalTransaction { internal const long NullTransactionId = 0; private TransactionState _transactionState; - private TransactionType _transactionType; + private readonly TransactionType _transactionType; private long _transactionId; // passed in the MARS headers private int _openResultCount; // passed in the MARS headers private SqlInternalConnection _innerConnection; private bool _disposing; // used to prevent us from throwing exceptions while we're disposing private WeakReference _parent; // weak ref to the outer transaction object; needs to be weak to allow GC to occur. - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = Interlocked.Increment(ref _objectTypeCount); + private static int s_objectTypeCount; // EventSource counter + internal readonly int _objectID = Interlocked.Increment(ref s_objectTypeCount); internal bool RestoreBrokenConnection { get; set; } internal bool ConnectionHasBeenRestored { get; set; } @@ -66,81 +66,37 @@ internal SqlInternalTransaction(SqlInternalConnection innerConnection, Transacti ConnectionHasBeenRestored = false; } - internal bool HasParentTransaction - { - get - { + internal bool HasParentTransaction => // Return true if we are an API started local transaction, or if we were a TSQL // started local transaction and were then wrapped with a parent transaction as // a result of a later API begin transaction. - bool result = ((TransactionType.LocalFromAPI == _transactionType) || - (TransactionType.LocalFromTSQL == _transactionType && _parent != null)); - return result; - } - } + (_transactionType == TransactionType.LocalFromAPI) || + (_transactionType == TransactionType.LocalFromTSQL && _parent != null); - internal bool IsAborted - { - get - { - return (TransactionState.Aborted == _transactionState); - } - } + internal bool IsAborted => _transactionState == TransactionState.Aborted; - internal bool IsActive - { - get - { - return (TransactionState.Active == _transactionState); - } - } + internal bool IsActive => _transactionState == TransactionState.Active; - internal bool IsCommitted - { - get - { - return (TransactionState.Committed == _transactionState); - } - } + internal bool IsCommitted => _transactionState == TransactionState.Committed; - internal bool IsCompleted - { - get - { - return (TransactionState.Aborted == _transactionState - || TransactionState.Committed == _transactionState - || TransactionState.Unknown == _transactionState); - } - } + internal bool IsCompleted => _transactionState == TransactionState.Aborted + || _transactionState == TransactionState.Committed + || _transactionState == TransactionState.Unknown; - internal bool IsDelegated - { - get - { - bool result = (TransactionType.Delegated == _transactionType); - return result; - } - } + internal bool IsDelegated =>_transactionType == TransactionType.Delegated; - internal bool IsDistributed - { - get - { - bool result = (TransactionType.Distributed == _transactionType); - return result; - } - } + internal bool IsDistributed => _transactionType == TransactionType.Distributed; - internal bool IsLocal - { - get - { - bool result = (TransactionType.LocalFromTSQL == _transactionType - || TransactionType.LocalFromAPI == _transactionType - ); - return result; - } - } +#if NETFRAMEWORK + internal bool IsContext => _transactionType == TransactionType.Context; +#endif + + internal bool IsLocal => _transactionType == TransactionType.LocalFromTSQL + || _transactionType == TransactionType.LocalFromAPI +#if NETFRAMEWORK + || IsContext +#endif + ; internal bool IsOrphaned { @@ -149,7 +105,7 @@ internal bool IsOrphaned // An internal transaction is orphaned when its parent has been // reclaimed by GC. bool result; - if (null == _parent) + if (_parent == null) { // No parent, so we better be LocalFromTSQL. Should we even return in this case - // since it could be argued this is invalid? @@ -172,29 +128,11 @@ internal bool IsOrphaned } } - internal bool IsZombied - { - get - { - return (null == _innerConnection); - } - } + internal bool IsZombied => _innerConnection == null; - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID => _objectID; - internal int OpenResultsCount - { - get - { - return _openResultCount; - } - } + internal int OpenResultsCount => _openResultCount; internal SqlTransaction Parent { @@ -202,7 +140,7 @@ internal SqlTransaction Parent { SqlTransaction result = null; // Should we protect against this, since this probably is an invalid state? - Debug.Assert(null != _parent, "Why are we calling Parent with no parent?"); + Debug.Assert(_parent != null, "Why are we calling Parent with no parent?"); if (_parent != null && _parent.TryGetTarget(out SqlTransaction target)) { result = target; @@ -213,10 +151,7 @@ internal SqlTransaction Parent internal long TransactionId { - get - { - return _transactionId; - } + get => _transactionId; set { Debug.Assert(NullTransactionId == _transactionId, "setting transaction cookie while one is active?"); @@ -224,10 +159,7 @@ internal long TransactionId } } - internal void Activate() - { - _transactionState = TransactionState.Active; - } + internal void Activate() => _transactionState = TransactionState.Active; private void CheckTransactionLevelAndZombie() { @@ -245,6 +177,10 @@ private void CheckTransactionLevelAndZombie() { throw; } +#if NETFRAMEWORK + ADP.TraceExceptionWithoutRethrow(e); +#endif + Zombie(); // If exception caught when trying to check level, zombie. } } @@ -267,6 +203,9 @@ internal void CloseFromConnection() } finally { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method +#endif if (processFinallyBlock) { // Always ensure we're zombied; 2005 will send an EnvChange that @@ -295,6 +234,18 @@ internal void Commit() // COMMIT ignores transaction names, and so there is no reason to pass it anything. COMMIT // simply commits the transaction from the most recent BEGIN, nested or otherwise. _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Commit, null, IsolationLevel.Unspecified, null, false); +#if NETFRAMEWORK + // SQL BU DT 291159 - perform full Zombie on pre-2005, but do not actually + // complete internal transaction until informed by server in the case of 2005 + // or later. + if (!IsZombied && !_innerConnection.Is2005OrNewer) + { + // Since nested transactions are no longer allowed, set flag to false. + // This transaction has been completed. + Zombie(); + } + else +#endif { ZombieParent(); } @@ -330,8 +281,8 @@ internal int DecrementAndObtainOpenResultCount() internal void Dispose() { - this.Dispose(true); - System.GC.SuppressFinalize(this); + Dispose(true); + GC.SuppressFinalize(this); } private void Dispose(bool disposing) @@ -339,7 +290,7 @@ private void Dispose(bool disposing) SqlClientEventSource.Log.TryPoolerTraceEvent("SqlInternalTransaction.Dispose | RES | CPOOL | Object Id {0}, Disposing", ObjectID); if (disposing) { - if (null != _innerConnection) + if (_innerConnection != null) { // implicitly rollback if transaction still valid _disposing = true; @@ -347,13 +298,14 @@ private void Dispose(bool disposing) } } } - + /// + /// This function is needed for those times when it is impossible to determine the server's + /// transaction level, unless the user's arguments were parsed - which is something we don't want + ///to do. An example when it is impossible to determine the level is after a rollback. + /// + /// private int GetServerTransactionLevel() { - // This function is needed for those times when it is impossible to determine the server's - // transaction level, unless the user's arguments were parsed - which is something we don't want - // to do. An example when it is impossible to determine the level is after a rollback. - using (SqlCommand transactionLevelCommand = new SqlCommand("set @out = @@trancount", (SqlConnection)(_innerConnection.Owner))) { transactionLevelCommand.Transaction = Parent; @@ -361,8 +313,7 @@ private int GetServerTransactionLevel() SqlParameter parameter = new SqlParameter("@out", SqlDbType.Int); parameter.Direction = ParameterDirection.Output; transactionLevelCommand.Parameters.Add(parameter); - - transactionLevelCommand.RunExecuteReader(CommandBehavior.Default, RunBehavior.UntilDone, returnStream: false); + transactionLevelCommand.RunExecuteReader(CommandBehavior.Default, RunBehavior.UntilDone, returnStream: false, nameof(GetServerTransactionLevel)); return (int)parameter.Value; } @@ -447,6 +398,15 @@ internal void Rollback(string transactionName) try { _innerConnection.ExecuteTransaction(SqlInternalConnection.TransactionRequest.Rollback, transactionName, IsolationLevel.Unspecified, null, false); +#if NETFRAMEWORK + if (!IsZombied && !_innerConnection.Is2005OrNewer) + { + // Check if Zombied before making round-trip to server. + // Against 2005 we receive an envchange on the ExecuteTransaction above on the + // parser that calls back into SqlTransaction for the Zombie() call. + CheckTransactionLevelAndZombie(); + } +#endif } catch (Exception e) { @@ -509,7 +469,8 @@ internal void Zombie() // Number 1 needs to be done whenever a SqlTransaction object is completed. Number // 2 is only done when a transaction is actually completed. Since users can begin // transactions both in and outside of the API, and since nested begins are not actual - // transactions we need to distinguish between #1 and #2. + // transactions we need to distinguish between #1 and #2.See SQL BU DT 291159 + // for further details. ZombieParent(); @@ -526,15 +487,15 @@ private void ZombieParent() { if (_parent != null && _parent.TryGetTarget(out SqlTransaction parent)) { - parent.Zombie(); + parent.Zombie(); } _parent = null; } - internal string TraceString() - { - return string.Format(/*IFormatProvider*/ null, "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", - ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); - } + internal string TraceString() => string.Format(/*IFormatProvider*/ null, + "(ObjId={0}, tranId={1}, state={2}, type={3}, open={4}, disp={5}", + ObjectID, _transactionId, _transactionState, _transactionType, _openResultCount, _disposing); + + } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs similarity index 90% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs index 9845a03aa7..c78ef7dfc7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -44,7 +44,6 @@ internal TextDataFeed(TextReader source) { _source = source; } - internal static UnicodeEncoding DefaultEncoding { get @@ -64,13 +63,10 @@ internal class XmlDataFeed : DataFeed { internal XmlReader _source; - internal XmlDataFeed(XmlReader source) - { - _source = source; - } + internal XmlDataFeed(XmlReader source) => _source = source; } - /// + /// [TypeConverter(typeof(SqlParameter.SqlParameterConverter))] public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable { @@ -247,7 +243,7 @@ private enum SqlParameterFlags : ushort private DataRowVersion _sourceVersion; private SqlParameterFlags _flags; - /// + /// public SqlParameter() : base() { _flags = SqlParameterFlags.IsNull; @@ -255,14 +251,14 @@ public SqlParameter() : base() _direction = ParameterDirection.Input; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType) : this() { ParameterName = parameterName; SqlDbType = dbType; } - /// + /// public SqlParameter(string parameterName, object value) : this() { Debug.Assert(!(value is SqlDbType), "use SqlParameter(string, SqlDbType)"); @@ -271,7 +267,7 @@ public SqlParameter(string parameterName, object value) : this() Value = value; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() { ParameterName = parameterName; @@ -279,7 +275,7 @@ public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() Size = size; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn) : this() { ParameterName = parameterName; @@ -288,7 +284,7 @@ public SqlParameter(string parameterName, SqlDbType dbType, int size, string sou SourceColumn = sourceColumn; } - /// + /// [EditorBrowsable(EditorBrowsableState.Advanced)] public SqlParameter( string parameterName, @@ -306,13 +302,18 @@ object value { Direction = direction; IsNullable = isNullable; +#if NETFRAMEWORK + PrecisionInternal = precision; + ScaleInternal = scale; +#else Precision = precision; Scale = scale; +#endif SourceVersion = sourceVersion; Value = value; } - /// + /// public SqlParameter( string parameterName, SqlDbType dbType, @@ -334,8 +335,13 @@ string xmlSchemaCollectionName SqlDbType = dbType; Size = size; Direction = direction; +#if NETFRAMEWORK + PrecisionInternal = precision; + ScaleInternal = scale; +#else Precision = precision; Scale = scale; +#endif SourceColumn = sourceColumn; SourceVersion = sourceVersion; SourceColumnNullMapping = sourceColumnNullMapping; @@ -381,7 +387,7 @@ internal bool HasReceivedMetadata /// internal byte NormalizationRuleVersion => CipherMetadata?.NormalizationRuleVersion ?? 0x00; - /// + /// [Browsable(false)] public SqlCompareOptions CompareInfo { @@ -417,34 +423,34 @@ public SqlCompareOptions CompareInfo } } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionDatabase { get => _xmlSchemaCollection?.Database ?? string.Empty; set => EnsureXmlSchemaCollection().Database = value; } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionOwningSchema { get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; set => EnsureXmlSchemaCollection().OwningSchema = value; } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionName { get => _xmlSchemaCollection?.Name ?? string.Empty; set => EnsureXmlSchemaCollection().Name = value; } - /// + /// [ DefaultValue(false), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public bool ForceColumnEncryption { @@ -452,7 +458,7 @@ public bool ForceColumnEncryption set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); } - /// + /// public override DbType DbType { get => GetMetaTypeOnly().DbType; @@ -467,11 +473,11 @@ public override DbType DbType } } - /// + /// public override void ResetDbType() => ResetSqlDbType(); - /// - [ResCategory("Data")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] public override string ParameterName { get => _parameterName ?? string.Empty; @@ -499,7 +505,7 @@ public override string ParameterName } } - /// + /// [Browsable(false)] public int LocaleId { @@ -529,10 +535,10 @@ public int LocaleId } } - /// + /// [ DefaultValue((byte)0), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public new byte Precision { @@ -542,10 +548,10 @@ public int LocaleId private bool ShouldSerializePrecision() => _precision != 0; - /// + /// [ DefaultValue((byte)0), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public new byte Scale { @@ -579,10 +585,10 @@ internal byte ScaleInternal private bool ShouldSerializeScale() => _scale != 0; // V1.0 compat, ignore _hasScale - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), DbProviderSpecificTypeProperty(true) ] public SqlDbType SqlDbType @@ -612,7 +618,7 @@ public SqlDbType SqlDbType private bool ShouldSerializeSqlDbType() => _metaType != null; - /// + /// public void ResetSqlDbType() { if (_metaType != null) @@ -622,7 +628,7 @@ public void ResetSqlDbType() } } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -673,7 +679,7 @@ public object SqlValue } } - /// + /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) @@ -684,7 +690,7 @@ public string UdtTypeName set => _udtTypeName = value; } - /// + /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) @@ -699,10 +705,10 @@ public string TypeName } } - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), TypeConverter(typeof(StringConverter)), ] public override object Value @@ -741,10 +747,10 @@ public override object Value } } - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), ] public override ParameterDirection Direction { @@ -769,14 +775,14 @@ public override ParameterDirection Direction } } - /// + /// public override bool IsNullable { get => HasFlag(SqlParameterFlags.IsNullable); set => SetFlag(SqlParameterFlags.IsNullable, value); } - /// + /// public int Offset { get => _offset; @@ -790,8 +796,8 @@ public int Offset } } - /// - [ResCategory("Data")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] public override int Size { get @@ -828,28 +834,30 @@ private void ResetSize() private bool ShouldSerializeSize() => _size != 0; - /// - [ResCategory("Update")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] public override string SourceColumn { get => _sourceColumn ?? string.Empty; set => _sourceColumn = value; } - /// + /// [ResCategory("DataCategory_Update")] +#if !NETFRAMEWORK [ResDescription(StringsHelper.ResourceNames.SqlParameter_SourceColumnNullMapping)] +#endif public override bool SourceColumnNullMapping { get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); } - /// + /// [ResCategory("Data")] public override string ToString() => ParameterName; - /// + /// [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] public override DataRowVersion SourceVersion { @@ -874,9 +882,11 @@ public override DataRowVersion SourceVersion } } - /// + + /// object ICloneable.Clone() => new SqlParameter(this); + private object CoercedValue { get => _coercedValue; @@ -993,6 +1003,8 @@ internal string ParameterNameFixed } } + internal bool SizeInferred => 0 == _size; + internal INullable ValueAsINullable => _valueAsINullable; internal bool IsDerivedParameterTypeName @@ -1048,6 +1060,10 @@ internal void CopyTo(SqlParameter destination) internal object CompareExchangeParent(object value, object comparand) { + // the interlock guarantees same parameter won't belong to multiple collections + // at the same time, but to actually occur the user must really try + // since we never declared thread safety, we don't care at this time + //return System.Threading.Interlocked.CompareExchange(ref _parent, value, comparand); object parent = _parent; if (comparand == parent) { @@ -1500,7 +1516,7 @@ internal int GetActualSize() case SqlDbType.NText: case SqlDbType.Xml: { - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType)) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1515,7 +1531,7 @@ internal int GetActualSize() case SqlDbType.Text: { // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1528,7 +1544,7 @@ internal int GetActualSize() case SqlDbType.VarBinary: case SqlDbType.Image: case SqlDbType.Timestamp: - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -1539,7 +1555,12 @@ internal int GetActualSize() case SqlDbType.Udt: if (!IsNull) { +#if NETFRAMEWORK + //call the static function + coercedSize = AssemblyCache.GetLength(val); +#else coercedSize = SerializationHelperSql9.SizeInBytes(val); +#endif } break; case SqlDbType.Structured: @@ -1671,6 +1692,7 @@ internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhe maxLen = -1; } + int localeId = LocaleId; if (localeId == 0 && mt.IsCharType) { @@ -1866,10 +1888,7 @@ internal void Prepare(SqlCommand cmd) } } - private void PropertyChanging() - { - _internalMetaType = null; - } + private void PropertyChanging() => _internalMetaType = null; private void PropertyTypeChanging() { @@ -1879,6 +1898,11 @@ private void PropertyTypeChanging() internal void ResetParent() => _parent = null; + private void SetFlag(SqlParameterFlags flag, bool value) + { + _flags = value ? _flags | flag : _flags & ~flag; + } + internal void SetSqlBuffer(SqlBuffer buff) { _sqlBufferReturnValue = buff; @@ -1891,15 +1915,7 @@ internal void SetSqlBuffer(SqlBuffer buff) _actualSize = -1; } - private void SetFlag(SqlParameterFlags flag, bool value) - { - _flags = value ? _flags | flag : _flags & ~flag; - } - - internal void SetUdtLoadError(Exception e) - { - _udtLoadError = e; - } + internal void SetUdtLoadError(Exception e) => _udtLoadError = e; internal void Validate(int index, bool isCommandProc) { @@ -1916,6 +1932,8 @@ internal void Validate(int index, bool isCommandProc) ((null == _value) || Convert.IsDBNull(_value)) && (SqlDbType != SqlDbType.Timestamp) && (SqlDbType != SqlDbType.Udt) && + // BUG: (VSTFDevDiv - 479609): Output parameter with size 0 throws for XML, TEXT, NTEXT, IMAGE. + // NOTE: (VSTFDevDiv - 479609): Not Fixed for TEXT, NTEXT, IMAGE as these are deprecated LOB types. (SqlDbType != SqlDbType.Xml) && !metaType.IsVarTime ) @@ -1980,6 +1998,24 @@ internal MetaType ValidateTypeLengths() long actualSizeInBytes = GetActualSize(); long sizeInCharacters = Size; + // Bug: VSTFDevDiv #636867 + // Notes: + // 'actualSizeInBytes' is the size of value passed; + // 'sizeInCharacters' is the parameter size; + // 'actualSizeInBytes' is in bytes; + // 'this.Size' is in charaters; + // 'sizeInCharacters' is in characters; + // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes; + // For Non-NCharType and for non-2005 or greater variables, size should be maintained; + // Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression; + // Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and + // 'maxSize' to 'maxSizeInBytes' + // The idea is to + // 1) revert the regression from bug 479739 + // 2) fix as many scenarios as possible including bug 636867 + // 3) cause no additional regression from 3.5 sp1 + // Keeping these goals in mind - the following are the changes we are making + long maxSizeInBytes; if (mt.IsNCharType) { @@ -1998,9 +2034,8 @@ internal MetaType ValidateTypeLengths() HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || (sizeInCharacters == -1) || (actualSizeInBytes == -1) - ) - { // is size > size able to be described by 2 bytes - // Convert the parameter to its max type + ) + { mt = MetaType.GetMaxMetaTypeFromMetaType(mt); _metaType = mt; InternalMetaType = mt; @@ -2131,6 +2166,7 @@ private int ValueSizeCore(object value) return 0; } + // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata) internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs index 4dabe4351d..ced6e62755 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs @@ -65,8 +65,10 @@ internal static string GetSHA256Hash(byte[] input) /// A byte array containing cryptographically generated random bytes internal static void GenerateRandomBytes(byte[] randomBytes) { - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } } /// @@ -367,7 +369,7 @@ internal static void VerifyColumnMasterKeySignature(string keyStoreName, string GetListOfProviderNamesThatWereSearched(connection, command)); } - if (ShouldUseInstanceLevelProviderFlow(keyStoreName,connection, command)) + if (ShouldUseInstanceLevelProviderFlow(keyStoreName, connection, command)) { isValidSignature = provider.VerifyColumnMasterKeyMetadata(keyPath, isEnclaveEnabled, CMKSignature); } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs similarity index 94% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs index d6ac49f927..436ad67c8e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs @@ -14,13 +14,13 @@ namespace Microsoft.Data.SqlClient sealed internal class SqlSequentialTextReader : System.IO.TextReader { private SqlDataReader _reader; // The SqlDataReader that we are reading data from - private int _columnIndex; // The index of out column in the table - private Encoding _encoding; // Encoding for this character stream - private Decoder _decoder; // Decoder based on the encoding (NOTE: Decoders are stateful as they are designed to process streams of data) + private readonly int _columnIndex; // The index of out column in the table + private readonly Encoding _encoding; // Encoding for this character stream + private readonly Decoder _decoder; // Decoder based on the encoding (NOTE: Decoders are stateful as they are designed to process streams of data) private byte[] _leftOverBytes; // Bytes leftover from the last Read() operation - this can be null if there were no bytes leftover (Possible optimization: re-use the same array?) private int _peekedChar; // The last character that we peeked at (or -1 if we haven't peeked at anything) private Task _currentTask; // The current async task - private CancellationTokenSource _disposalTokenSource; // Used to indicate that a cancellation is requested due to disposal + private readonly CancellationTokenSource _disposalTokenSource; // Used to indicate that a cancellation is requested due to disposal internal SqlSequentialTextReader(SqlDataReader reader, int columnIndex, Encoding encoding) { @@ -38,10 +38,7 @@ internal SqlSequentialTextReader(SqlDataReader reader, int columnIndex, Encoding _disposalTokenSource = new CancellationTokenSource(); } - internal int ColumnIndex - { - get { return _columnIndex; } - } + internal int ColumnIndex => _columnIndex; public override int Peek() { @@ -131,7 +128,7 @@ public override int Read(char[] buffer, int index, int count) public override Task ReadAsync(char[] buffer, int index, int count) { ValidateReadParameters(buffer, index, count); - TaskCompletionSource completion = new TaskCompletionSource(); + TaskCompletionSource completion = new(); if (IsClosed) { @@ -169,17 +166,15 @@ public override Task ReadAsync(char[] buffer, int index, int count) } } - int byteBufferUsed; - byte[] byteBuffer = PrepareByteBuffer(charsNeeded, out byteBufferUsed); + byte[] byteBuffer = PrepareByteBuffer(charsNeeded, out int byteBufferUsed); // Permit a 0 byte read in order to advance the reader to the correct column if ((byteBufferUsed < byteBuffer.Length) || (byteBuffer.Length == 0)) { - int bytesRead; - var reader = _reader; + SqlDataReader reader = _reader; if (reader != null) { - Task getBytesTask = reader.GetBytesAsync(_columnIndex, byteBuffer, byteBufferUsed, byteBuffer.Length - byteBufferUsed, Timeout.Infinite, _disposalTokenSource.Token, out bytesRead); + Task getBytesTask = reader.GetBytesAsync(_columnIndex, byteBuffer, byteBufferUsed, byteBuffer.Length - byteBufferUsed, Timeout.Infinite, _disposalTokenSource.Token, out int bytesRead); if (getBytesTask == null) { byteBufferUsed += bytesRead; @@ -295,7 +290,7 @@ internal void SetClosed() _peekedChar = -1; // Wait for pending task - var currentTask = _currentTask; + Task currentTask = _currentTask; if (currentTask != null) { ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne(); @@ -318,8 +313,7 @@ private int InternalRead(char[] buffer, int index, int count) try { - int byteBufferUsed; - byte[] byteBuffer = PrepareByteBuffer(count, out byteBufferUsed); + byte[] byteBuffer = PrepareByteBuffer(count, out int byteBufferUsed); byteBufferUsed += _reader.GetBytesInternalSequential(_columnIndex, byteBuffer, byteBufferUsed, byteBuffer.Length - byteBufferUsed); if (byteBufferUsed > 0) @@ -402,10 +396,7 @@ private int DecodeBytesToChars(byte[] inBuffer, int inBufferCount, char[] outBuf Debug.Assert(outBuffer != null, "Null output buffer"); Debug.Assert((outBufferOffset >= 0) && (outBufferCount > 0) && (outBufferOffset + outBufferCount <= outBuffer.Length), $"Bad outBufferCount: {outBufferCount} or outBufferOffset: {outBufferOffset}"); - int charsRead; - int bytesUsed; - bool completed; - _decoder.Convert(inBuffer, 0, inBufferCount, outBuffer, outBufferOffset, outBufferCount, false, out bytesUsed, out charsRead, out completed); + _decoder.Convert(inBuffer, 0, inBufferCount, outBuffer, outBufferOffset, outBufferCount, false, out int bytesUsed, out int charsRead, out bool completed); // completed may be false and there is no spare bytes if the Decoder has stored bytes to use later if ((!completed) && (bytesUsed < inBufferCount)) @@ -479,7 +470,7 @@ internal static void ValidateReadParameters(char[] buffer, int index, int count) sealed internal class SqlUnicodeEncoding : UnicodeEncoding { - private static SqlUnicodeEncoding s_singletonEncoding = new SqlUnicodeEncoding(); + private static readonly SqlUnicodeEncoding s_singletonEncoding = new(); private SqlUnicodeEncoding() : base(bigEndian: false, byteOrderMark: false, throwOnInvalidBytes: false) { } @@ -511,10 +502,7 @@ public override int GetCharCount(byte[] bytes, int index, int count) public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { // This method is required - simply call Convert() - int bytesUsed; - int charsUsed; - bool completed; - Convert(bytes, byteIndex, byteCount, chars, charIndex, chars.Length - charIndex, true, out bytesUsed, out charsUsed, out completed); + Convert(bytes, byteIndex, byteCount, chars, charIndex, chars.Length - charIndex, true, out int bytesUsed, out int charsUsed, out bool completed); return charsUsed; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStream.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStream.cs similarity index 86% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStream.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStream.cs index cab3ba9350..4c78cc4c85 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStream.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStream.cs @@ -16,12 +16,12 @@ namespace Microsoft.Data.SqlClient sealed internal class SqlStream : Stream { private SqlDataReader _reader; // reader we will stream off - private int _columnOrdinal; + private readonly int _columnOrdinal; private long _bytesCol; private int _bom; private byte[] _bufferedData; - private bool _processAllRows; - private bool _advanceReader; + private readonly bool _processAllRows; + private readonly bool _advanceReader; private bool _readFirstRow = false; private bool _endOfColumn = false; @@ -39,51 +39,21 @@ internal SqlStream(int columnOrdinal, SqlDataReader reader, bool addByteOrderMar _advanceReader = advanceReader; } - override public bool CanRead - { - get - { - return true; - } - } + public override bool CanRead => true; - override public bool CanSeek - { - get - { - return false; - } - } + public override bool CanSeek => false; - override public bool CanWrite - { - get - { - return false; - } - } + public override bool CanWrite => false; - override public long Length - { - get - { - throw ADP.NotSupported(); - } - } + public override long Length => throw ADP.NotSupported(); - override public long Position + public override long Position { - get - { - throw ADP.NotSupported(); - } - set - { - throw ADP.NotSupported(); - } + get => throw ADP.NotSupported(); + set => throw ADP.NotSupported(); } - override protected void Dispose(bool disposing) + protected override void Dispose(bool disposing) { try { @@ -99,17 +69,14 @@ override protected void Dispose(bool disposing) } } - override public void Flush() - { - throw ADP.NotSupported(); - } + public override void Flush() => throw ADP.NotSupported(); - override public int Read(byte[] buffer, int offset, int count) + public override int Read(byte[] buffer, int offset, int count) { int intCount = 0; int cBufferedData = 0; - if ((null == _reader)) + if (null == _reader) { throw ADP.StreamClosed(); } @@ -201,7 +168,6 @@ private int ReadBytes(byte[] buffer, int offset, int count) { bool gotData = true; int intCount = 0; - int cb = 0; if (_reader.IsClosed || _endOfColumn) { @@ -240,8 +206,7 @@ private int ReadBytes(byte[] buffer, int offset, int count) if (gotData) { - cb = (int)_reader.GetBytesInternal(_columnOrdinal, _bytesCol, buffer, offset, count); - + int cb = (int)_reader.GetBytesInternal(_columnOrdinal, _bytesCol, buffer, offset, count); if (cb < count) { _bytesCol = 0; @@ -290,20 +255,11 @@ internal XmlReader ToXmlReader(bool async = false) return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(this, closeInput: true, async: async); } - override public long Seek(long offset, SeekOrigin origin) - { - throw ADP.NotSupported(); - } + public override long Seek(long offset, SeekOrigin origin) => throw ADP.NotSupported(); - override public void SetLength(long value) - { - throw ADP.NotSupported(); - } + public override void SetLength(long value) => throw ADP.NotSupported(); - override public void Write(byte[] buffer, int offset, int count) - { - throw ADP.NotSupported(); - } + public override void Write(byte[] buffer, int offset, int count) => throw ADP.NotSupported(); } @@ -324,39 +280,15 @@ internal SqlCachedStream(SqlCachedBuffer sqlBuf) _cachedBytes = sqlBuf.CachedBytes; } - override public bool CanRead - { - get - { - return true; - } - } + public override bool CanRead => true; - override public bool CanSeek - { - get - { - return true; - } - } + public override bool CanSeek => true; - override public bool CanWrite - { - get - { - return false; - } - } + public override bool CanWrite => false; - override public long Length - { - get - { - return TotalLength; - } - } + public override long Length => TotalLength; - override public long Position + public override long Position { get { @@ -381,7 +313,7 @@ override public long Position } } - override protected void Dispose(bool disposing) + protected override void Dispose(bool disposing) { try { @@ -398,12 +330,9 @@ override protected void Dispose(bool disposing) } } - override public void Flush() - { - throw ADP.NotSupported(); - } + public override void Flush() => throw ADP.NotSupported(); - override public int Read(byte[] buffer, int offset, int count) + public override int Read(byte[] buffer, int offset, int count) { int cb; int intCount = 0; @@ -450,8 +379,8 @@ override public int Read(byte[] buffer, int offset, int count) cb = _cachedBytes[_currentArrayIndex].Length - _currentPosition; if (cb > count) cb = count; - Buffer.BlockCopy(_cachedBytes[_currentArrayIndex], _currentPosition, buffer, offset, cb); + Buffer.BlockCopy(_cachedBytes[_currentArrayIndex], _currentPosition, buffer, offset, cb); _currentPosition += cb; count -= (int)cb; offset += (int)cb; @@ -461,7 +390,7 @@ override public int Read(byte[] buffer, int offset, int count) return intCount; } - override public long Seek(long offset, SeekOrigin origin) + public override long Seek(long offset, SeekOrigin origin) { long pos = 0; @@ -492,15 +421,9 @@ override public long Seek(long offset, SeekOrigin origin) return pos; } - override public void SetLength(long value) - { - throw ADP.NotSupported(); - } + public override void SetLength(long value) => throw ADP.NotSupported(); - override public void Write(byte[] buffer, int offset, int count) - { - throw ADP.NotSupported(); - } + public override void Write(byte[] buffer, int offset, int count) => throw ADP.NotSupported(); private void SetInternalPosition(long lPos, string argumentName) { @@ -547,7 +470,7 @@ private long TotalLength sealed internal class SqlStreamingXml { - private int _columnOrdinal; + private readonly int _columnOrdinal; private SqlDataReader _reader; private XmlReader _xmlReader; private XmlWriter _xmlWriter; @@ -570,22 +493,16 @@ public void Close() _strWriter = null; } - public int ColumnOrdinal - { - get - { - return _columnOrdinal; - } - } + public int ColumnOrdinal => _columnOrdinal; public long GetChars(long dataIndex, char[] buffer, int bufferIndex, int length) { if (_xmlReader == null) { - SqlStream sqlStream = new SqlStream(_columnOrdinal, _reader, true /* addByteOrderMark */, false /* processAllRows*/, false /*advanceReader*/); + SqlStream sqlStream = new(_columnOrdinal, _reader, addByteOrderMark: true, processAllRows:false, advanceReader:false); _xmlReader = sqlStream.ToXmlReader(); _strWriter = new StringWriter((System.IFormatProvider)null); - XmlWriterSettings writerSettings = new XmlWriterSettings(); + XmlWriterSettings writerSettings = new(); writerSettings.CloseOutput = true; // close the memory stream when done writerSettings.ConformanceLevel = ConformanceLevel.Fragment; _xmlWriter = XmlWriter.Create(_strWriter, writerSettings); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs new file mode 100644 index 0000000000..19996684b9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Data; +using System.Data.Common; +using System.Diagnostics; +using Microsoft.Data.Common; + +namespace Microsoft.Data.SqlClient +{ + /// + public sealed partial class SqlTransaction : DbTransaction + { + private static int s_objectTypeCount; // EventSource Counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount); + internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; + + private SqlInternalTransaction _internalTransaction; + private readonly SqlConnection _connection; + + private bool _isFromAPI; + + internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, + IsolationLevel iso, SqlInternalTransaction internalTransaction) + { +#if NETFRAMEWORK + SqlConnection.VerifyExecutePermission(); +#endif + _isolationLevel = iso; + _connection = con; + + if (internalTransaction == null) + { + _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); + } + else + { + Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); + _internalTransaction = internalTransaction; + _internalTransaction.InitParent(this); + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // PROPERTIES + //////////////////////////////////////////////////////////////////////////////////////// + + /// + public new SqlConnection Connection + {// MDAC 66655 + get + { + if (IsZombied) + { + return null; + } + else + { + return _connection; + } + } + } + + /// + protected override DbConnection DbConnection => Connection; + + internal SqlInternalTransaction InternalTransaction => _internalTransaction; + + /// + public override IsolationLevel IsolationLevel + { + get + { + ZombieCheck(); + return _isolationLevel; + } + } + + private bool Is2005PartialZombie => _internalTransaction !=null && _internalTransaction.IsCompleted; + + internal bool IsZombied => _internalTransaction == null || _internalTransaction.IsCompleted; + + internal int ObjectID => _objectID; + + internal SqlStatistics Statistics + { + get + { + if (null != _connection) + { + if (_connection.StatisticsEnabled) + { + return _connection.Statistics; + } + } + return null; + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // INTERNAL METHODS + //////////////////////////////////////////////////////////////////////////////////////// + + internal void Zombie() + { + // For Yukon, we have to defer "zombification" until + // we get past the users' next rollback, else we'll + // throw an exception there that is a breaking change. + // Of course, if the connection is already closed, + // then we're free to zombify... + SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); + if (null != internalConnection +#if NETFRAMEWORK + && internalConnection.Is2005OrNewer +#endif + && !_isFromAPI) + { + SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", ObjectID); + } + else + { + _internalTransaction = null; // pre SQL 2005 zombification + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // PRIVATE METHODS + //////////////////////////////////////////////////////////////////////////////////////// + + private void ZombieCheck() + { + // If this transaction has been completed, throw exception since it is unusable. + if (IsZombied) + { + if (Is2005PartialZombie) + { + _internalTransaction = null; // SQL 2005 zombification + } + + throw ADP.TransactionZombied(this); + } + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs index c87051dd8a..4ac81a9844 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs @@ -4,7 +4,8 @@ using System; using System.Collections.Generic; -using Microsoft.Data.SqlClient.Server; +using Microsoft.Data.Common; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient { @@ -32,7 +33,7 @@ internal static SqlUdtInfo GetFromType(Type target) SqlUdtInfo udtAttr = TryGetFromType(target); if (udtAttr == null) { - throw InvalidUdtException.Create(target, Strings.SqlUdtReason_NoUdtAttribute); + throw ADP.CreateInvalidUdtException(target, nameof(Strings.SqlUdtReason_NoUdtAttribute)); } return udtAttr; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs similarity index 85% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs index f8a95fbe17..81c2ed1570 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsEnums.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Data; using System.Diagnostics; +using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { @@ -17,9 +19,25 @@ internal static class TdsEnums public const string SQL_PROVIDER_NAME = Common.DbConnectionStringDefaults.ApplicationName; - public static readonly decimal SQL_SMALL_MONEY_MIN = new decimal(-214748.3648); - public static readonly decimal SQL_SMALL_MONEY_MAX = new decimal(214748.3647); - + public static readonly decimal SQL_SMALL_MONEY_MIN = new(-214748.3648); + public static readonly decimal SQL_SMALL_MONEY_MAX = new(214748.3647); + +#if NETFRAMEWORK + // sql debugging constants, sdci is the structure passed in + public const string SDCI_MAPFILENAME = "SqlClientSSDebug"; + public const byte SDCI_MAX_MACHINENAME = 32; + public const byte SDCI_MAX_DLLNAME = 16; + public const byte SDCI_MAX_DATA = 255; + public const int SQLDEBUG_OFF = 0; + public const int SQLDEBUG_ON = 1; + public const int SQLDEBUG_CONTEXT = 2; + public const string SP_SDIDEBUG = "sp_sdidebug"; + public static readonly string[] SQLDEBUG_MODE_NAMES = new string[3] { + "off", + "on", + "context" + }; +#endif // HACK!!! // Constant for SqlDbType.SmallVarBinary... store internal variable here instead of on @@ -208,6 +226,7 @@ public enum EnvChangeType : byte public const byte FEATUREEXT_TERMINATOR = 0xFF; public const byte FEATUREEXT_SRECOVERY = 0x01; public const byte FEATUREEXT_FEDAUTH = 0x02; + // 0x03 is for x_eFeatureExtensionId_Rcs public const byte FEATUREEXT_TCE = 0x04; public const byte FEATUREEXT_GLOBALTRANSACTIONS = 0x05; // 0x06 is for x_eFeatureExtensionId_LoginToken @@ -317,23 +336,33 @@ public enum ActiveDirectoryWorkflow : byte 0x72xx0002 -> 2005 RTM */ - - // 2000 SP1 and beyond versioning scheme: + // Pre 2000 SP1 versioning scheme: + public const int SQL70OR2000_MAJOR = 0x07; // The high byte (b3) is not sufficient to distinguish + public const int SQL70_INCREMENT = 0x00; // 7.0 and 2000 + public const int SQL2000_INCREMENT = 0x01; // So we need to look at the high-mid byte (b2) as well + public const int DEFAULT_MINOR = 0x0000; // Majors: + public const int SQL2000SP1_MAJOR = 0x71; // For 2000 SP1 and later the versioning schema changed and public const int SQL2005_MAJOR = 0x72; // the high-byte is sufficient to distinguish later versions public const int SQL2008_MAJOR = 0x73; - public const int SQl2012_MAJOR = 0x74; + public const int SQL2012_MAJOR = 0x74; + public const int TDS8_MAJOR = 0x08; // TDS8 version to be used at login7 + public const string TDS8_Protocol = "tds/8.0"; //TDS8 // Increments: + public const int SQL2000SP1_INCREMENT = 0x00; public const int SQL2005_INCREMENT = 0x09; public const int SQL2008_INCREMENT = 0x0b; public const int SQL2012_INCREMENT = 0x00; + public const int TDS8_INCREMENT = 0x00; // Minors: + public const int SQL2000SP1_MINOR = 0x0001; public const int SQL2005_RTM_MINOR = 0x0002; public const int SQL2008_MINOR = 0x0003; public const int SQL2012_MINOR = 0x0004; + public const int TDS8_MINOR = 0x00; public const int ORDER_68000 = 1; public const int USE_DB_ON = 1; @@ -478,6 +507,10 @@ public enum ActiveDirectoryWorkflow : byte public const byte TVP_ORDERDESC_FLAG = 0x2; public const byte TVP_UNIQUE_FLAG = 0x4; +#if NETFRAMEWORK + public const bool Is68K = false; + public const bool TraceTDS = false; +#endif // RPC function names public const string SP_EXECUTESQL = "sp_executesql"; // used against 7.0 servers @@ -559,6 +592,10 @@ public enum ActiveDirectoryWorkflow : byte // dbnetlib error values public const short TIMEOUT_EXPIRED = -2; public const short ENCRYPTION_NOT_SUPPORTED = 20; +#if NETFRAMEWORK + public const short CTAIP_NOT_SUPPORTED = 21; +#endif + // CAUTION: These are not error codes returned by SNI. This is used for backward compatibility // since netlib (now removed from sqlclient) returned these codes. @@ -590,6 +627,7 @@ public enum ActiveDirectoryWorkflow : byte public const uint SNI_SSL_VALIDATE_CERTIFICATE = 1; // This enables validation of server certificate public const uint SNI_SSL_USE_SCHANNEL_CACHE = 2; // This enables schannel session cache public const uint SNI_SSL_IGNORE_CHANNEL_BINDINGS = 0x10; // Used with SSL Provider, sent to SNIAddProvider in case of SQL Authentication & Encrypt. + public const uint SNI_SSL_SEND_ALPN_EXTENSION = 0x4000; // This flag instructs SSL provider to send the ALPN extension in the ClientHello message public const string DEFAULT_ENGLISH_CODE_PAGE_STRING = "iso_1"; public const short DEFAULT_ENGLISH_CODE_PAGE_VALUE = 1252; @@ -885,6 +923,13 @@ public enum ActiveDirectoryWorkflow : byte 0, /* 255 */ }; +#if NETFRAMEWORK + internal enum UDTFormatType + { + Native = 1, + UserDefined = 2 + } +#endif internal enum TransactionManagerRequestType { @@ -1061,106 +1106,107 @@ internal enum ParsingErrorState DataClassificationInvalidInformationTypeIndex = 27 } - /// + /// public enum SqlConnectionAttestationProtocol { - /// + /// NotSpecified = 0, - /// + /// AAS = 1, -#if ENCLAVE_SIMULATOR - /// - SIM = 2, -#endif + /// + None = 2, - /// + /// HGS = 3 } - /// + /// public enum SqlConnectionIPAddressPreference { - /// + /// IPv4First = 0, // default - /// + /// IPv6First = 1, - /// + /// UsePlatformDefault = 2 } - /// + /// public enum SqlConnectionColumnEncryptionSetting { - /// + /// Disabled = 0, - /// + /// Enabled, } - /// + /// [Flags] public enum SqlConnectionOverrides { - /// + /// None = 0, - /// + /// OpenWithoutRetry = 1, } - /// + /// public enum SqlCommandColumnEncryptionSetting { - /// + /// UseConnectionSetting = 0, - /// + /// Enabled, - /// + /// ResultSetOnly, - /// + /// Disabled, } - /// + /// public enum SqlAuthenticationMethod { - /// + /// NotSpecified = 0, - /// + /// SqlPassword, - /// + /// ActiveDirectoryPassword, - /// + /// ActiveDirectoryIntegrated, - /// + /// ActiveDirectoryInteractive, - /// + /// ActiveDirectoryServicePrincipal, - /// + /// ActiveDirectoryDeviceCodeFlow, - /// + /// ActiveDirectoryManagedIdentity, - /// + /// ActiveDirectoryMSI, - /// - ActiveDirectoryDefault + /// + ActiveDirectoryDefault, +#if ADONET_CERT_AUTH && NETFRAMEWORK + SqlCertificate +#endif } // This enum indicates the state of TransparentNetworkIPResolution // The first attempt when TNIR is on should be sequential. If the first attempt failes next attempts should be parallel. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs similarity index 66% rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs index 025ea7d020..93aac3fdea 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs @@ -3,10 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.Data.Common; using System.Globalization; +using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Security.Permissions; +using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { @@ -88,12 +88,19 @@ static internal void AliasRegistryLookup(ref string host, ref string protocol) } } - // Encrypt password to be sent to SQL Server + // Obfuscate password to be sent to SQL Server + // Blurb from the TDS spec at https://msdn.microsoft.com/en-us/library/dd304523.aspx + // "Before submitting a password from the client to the server, for every byte in the password buffer + // starting with the position pointed to by IbPassword, the client SHOULD first swap the four high bits + // with the four low bits and then do a bit-XOR with 0xA5 (10100101). After reading a submitted password, + // for every byte in the password buffer starting with the position pointed to by IbPassword, the server SHOULD + // first do a bit-XOR with 0xA5 (10100101) and then swap the four high bits with the four low bits." + // The password exchange during Login phase happens over a secure channel i.e. SSL/TLS // Note: The same logic is used in SNIPacketSetData (SniManagedWrapper) to encrypt passwords stored in SecureString // If this logic changed, SNIPacketSetData needs to be changed as well internal static byte[] ObfuscatePassword(string password) { - byte[] bEnc = new byte[password.Length << 1]; + byte[] bObfuscated = new byte[password.Length << 1]; int s; byte bLo; byte bHi; @@ -103,62 +110,92 @@ internal static byte[] ObfuscatePassword(string password) s = (int)password[i]; bLo = (byte)(s & 0xff); bHi = (byte)((s >> 8) & 0xff); - bEnc[i << 1] = (byte)((((bLo & 0x0f) << 4) | (bLo >> 4)) ^ 0xa5); - bEnc[(i << 1) + 1] = (byte)((((bHi & 0x0f) << 4) | (bHi >> 4)) ^ 0xa5); + bObfuscated[i << 1] = (byte)((((bLo & 0x0f) << 4) | (bLo >> 4)) ^ 0xa5); + bObfuscated[(i << 1) + 1] = (byte)((((bHi & 0x0f) << 4) | (bHi >> 4)) ^ 0xa5); } - return bEnc; + return bObfuscated; } - [ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only - [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] - static internal int GetCurrentProcessIdForTdsLoginOnly() + internal static byte[] ObfuscatePassword(byte[] password) { - return Common.SafeNativeMethods.GetCurrentProcessId(); + byte bLo; + byte bHi; + + for (int i = 0; i < password.Length; i++) + { + bLo = (byte)(password[i] & 0x0f); + bHi = (byte)(password[i] & 0xf0); + password[i] = (byte)(((bHi >> 4) | (bLo << 4)) ^ 0xa5); + } + return password; + } + + private const int NoProcessId = -1; + private static int s_currentProcessId = NoProcessId; + internal static int GetCurrentProcessIdForTdsLoginOnly() + { + if (s_currentProcessId == NoProcessId) + { + // Pick up the process Id from the current process instead of randomly generating it. + // This would be helpful while tracing application related issues. + int processId; + using (System.Diagnostics.Process p = System.Diagnostics.Process.GetCurrentProcess()) + { + processId = p.Id; + } + System.Threading.Volatile.Write(ref s_currentProcessId, processId); + } + return s_currentProcessId; } - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] - [ResourceExposure(ResourceScope.None)] // SxS: we use this method for TDS login only - [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] - static internal Int32 GetCurrentThreadIdForTdsLoginOnly() + internal static int GetCurrentThreadIdForTdsLoginOnly() { -#pragma warning disable 618 - return AppDomain.GetCurrentThreadId(); // don't need this to be support fibres; -#pragma warning restore 618 + return Environment.CurrentManagedThreadId; } + private static byte[] s_nicAddress = null; [ResourceExposure(ResourceScope.None)] // SxS: we use MAC address for TDS login only [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] static internal byte[] GetNetworkPhysicalAddressForTdsLoginOnly() { - // NIC address is stored in NetworkAddress key. However, if NetworkAddressLocal key - // has a value that is not zero, then we cannot use the NetworkAddress key and must - // instead generate a random one. I do not fully understand why, this is simply what - // the native providers do. As for generation, I use a random number generator, which - // means that different processes on the same machine will have different NIC address - // values on the server. It is not ideal, but native does not have the same value for - // different processes either. - - const string key = "NetworkAddress"; - const string localKey = "NetworkAddressLocal"; - const string folder = "SOFTWARE\\Description\\Microsoft\\Rpc\\UuidTemporaryData"; - - int result = 0; - byte[] nicAddress = null; - - object temp = ADP.LocalMachineRegistryValue(folder, localKey); - if (temp is int) + if (s_nicAddress != null) { - result = (int)temp; + return s_nicAddress; } - if (result <= 0) + byte[] nicAddress = null; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - temp = ADP.LocalMachineRegistryValue(folder, key); - if (temp is byte[]) + // NIC address is stored in NetworkAddress key. However, if NetworkAddressLocal key + // has a value that is not zero, then we cannot use the NetworkAddress key and must + // instead generate a random one. I do not fully understand why, this is simply what + // the native providers do. As for generation, I use a random number generator, which + // means that different processes on the same machine will have different NIC address + // values on the server. It is not ideal, but native does not have the same value for + // different processes either. + + const string key = "NetworkAddress"; + const string localKey = "NetworkAddressLocal"; + const string folder = "SOFTWARE\\Description\\Microsoft\\Rpc\\UuidTemporaryData"; + + int result = 0; + + object temp = ADP.LocalMachineRegistryValue(folder, localKey); + if (temp is int) { - nicAddress = (byte[])temp; + result = (int)temp; + } + + if (result <= 0) + { + temp = ADP.LocalMachineRegistryValue(folder, key); + if (temp is byte[]) + { + nicAddress = (byte[])temp; + } } } @@ -169,7 +206,9 @@ static internal byte[] GetNetworkPhysicalAddressForTdsLoginOnly() random.NextBytes(nicAddress); } - return nicAddress; + System.Threading.Interlocked.CompareExchange(ref s_nicAddress, nicAddress, null); + + return s_nicAddress; } // translates remaining time in stateObj (from user specified timeout) to timeout value for SNI @@ -221,6 +260,8 @@ internal static long GetTimeout(long timeoutMilliseconds) return result; } + internal static long GetTimeoutSeconds(int timeout) => GetTimeout((long)timeout * 1000L); + internal static bool TimeoutHasExpired(long timeoutTime) { bool result = false; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs index fa86cd7954..1ef8541721 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProvider.cs @@ -81,7 +81,7 @@ protected override byte[] MakeRequest(string url) } } - throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationSigningCertificateRequestFailedFormat, url, exception.Message), exception); + throw SQL.AttestationFailed(string.Format(Strings.GetAttestationSigningCertificateRequestFailedFormat, url, exception.Message), exception); } #endregion diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs index 6c940e3749..c4b9987f01 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/VirtualSecureModeEnclaveProviderBase.cs @@ -128,7 +128,7 @@ internal override void CreateEnclaveSession(byte[] attestationInfo, ECDiffieHell } else { - throw new AlwaysEncryptedAttestationException(Strings.FailToCreateEnclaveSession); + throw SQL.AttestationFailed(Strings.FailToCreateEnclaveSession); } } } @@ -173,7 +173,7 @@ private void VerifyAttestationInfo(string attestationUrl, HealthReport healthRep } else { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.VerifyHealthCertificateChainFormat, attestationUrl, chainStatus)); + throw SQL.AttestationFailed(string.Format(Strings.VerifyHealthCertificateChainFormat, attestationUrl, chainStatus)); } } } while (shouldRetryValidation); @@ -205,7 +205,7 @@ private X509Certificate2Collection GetSigningCertificate(string attestationUrl, } catch (CryptographicException exception) { - throw new AlwaysEncryptedAttestationException(String.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); + throw SQL.AttestationFailed(string.Format(Strings.GetAttestationSigningCertificateFailedInvalidCertificate, attestationUrl), exception); } rootSigningCertificateCache.Add(attestationUrl, certificateCollection, DateTime.Now.AddDays(1)); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs index 2cbd875cf8..853be887dc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Xml; using Microsoft.Data.SqlClient; @@ -19,12 +18,12 @@ namespace Microsoft.Data.SqlTypes /// this class provides ways to do that. We must review and update this implementation any time the /// implementation of the corresponding types in System.Data.Common change. /// - internal static class SqlTypeWorkarounds + internal static partial class SqlTypeWorkarounds { #region Work around inability to access SqlXml.CreateSqlXmlReader - private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment }; - private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; - private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new XmlReaderSettings() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; + private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new() { ConformanceLevel = ConformanceLevel.Fragment }; + private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; + private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; internal const SqlCompareOptions SqlStringValidSqlCompareOptionMask = SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | @@ -91,139 +90,5 @@ internal static DateTime SqlDateTimeToDateTime(int daypart, int timepart) private static Exception ThrowOverflowException() => throw SQL.DateTimeOverflow(); #endregion - - #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation - /// - /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists - /// only to distinguish this constructor from the constructor that takes a long. - /// Used only internally. - /// - internal static SqlMoney SqlMoneyCtor(long value, int ignored) - { - var c = default(SqlMoneyCaster); - - // Same behavior as the internal SqlMoney.ctor(long, bool) overload - c.Fake._fNotNull = true; - c.Fake._value = value; - - return c.Real; - } - - internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money) - { - var c = default(SqlMoneyCaster); - c.Real = money; - - // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation - if (money.IsNull) - { - throw new SqlNullValueException(); - } - return c.Fake._value; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields - { - internal bool _fNotNull; - internal long _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlMoneyCaster - { - [FieldOffset(0)] - internal SqlMoney Real; - [FieldOffset(0)] - internal SqlMoneyLookalike Fake; - } - #endregion - - #region Work around inability to access SqlDecimal._data1/2/3/4 - internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) - { - // Extract the four data elements from SqlDecimal. - var c = default(SqlDecimalCaster); - c.Real = d; - data1 = c.Fake._data1; - data2 = c.Fake._data2; - data3 = c.Fake._data3; - data4 = c.Fake._data4; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields - { - internal byte _bStatus; - internal byte _bLen; - internal byte _bPrec; - internal byte _bScale; - internal uint _data1; - internal uint _data2; - internal uint _data3; - internal uint _data4; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlDecimalCaster - { - [FieldOffset(0)] - internal SqlDecimal Real; - [FieldOffset(0)] - internal SqlDecimalLookalike Fake; - } - #endregion - - #region Work around inability to access SqlBinary.ctor(byte[], bool) - internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) - { - // Construct a SqlBinary without allocating/copying the byte[]. This provides - // the same behavior as SqlBinary.ctor(byte[], bool). - var c = default(SqlBinaryCaster); - c.Fake._value = value; - return c.Real; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlBinaryLookalike - { - internal byte[] _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlBinaryCaster - { - [FieldOffset(0)] - internal SqlBinary Real; - [FieldOffset(0)] - internal SqlBinaryLookalike Fake; - } - #endregion - - #region Work around inability to access SqlGuid.ctor(byte[], bool) - internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) - { - // Construct a SqlGuid without allocating/copying the byte[]. This provides - // the same behavior as SqlGuid.ctor(byte[], bool). - var c = default(SqlGuidCaster); - c.Fake._value = value; - return c.Real; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SqlGuidLookalike - { - internal byte[] _value; - } - - [StructLayout(LayoutKind.Explicit)] - private struct SqlGuidCaster - { - [FieldOffset(0)] - internal SqlGuid Real; - [FieldOffset(0)] - internal SqlGuidLookalike Fake; - } - #endregion } } diff --git a/src/Microsoft.Data.SqlClient/src/System/Diagnostics/CodeAnalysis.cs b/src/Microsoft.Data.SqlClient/src/System/Diagnostics/CodeAnalysis.cs new file mode 100644 index 0000000000..256f7cd1e0 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/System/Diagnostics/CodeAnalysis.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Diagnostics.CodeAnalysis +{ +#if NETSTANDARD2_0 + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)] + internal sealed class AllowNullAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)] + internal sealed class DisallowNullAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Method)] + internal sealed class DoesNotReturnAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + public bool ParameterValue { get; } + } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)] + internal sealed class MaybeNullAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + public bool ReturnValue { get; } + } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)] + internal sealed class NotNullAttribute : Attribute + { + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + public string ParameterName { get; } + } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class NotNullWhenAttribute : Attribute + { + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + public bool ReturnValue { get; } + } +#endif + +#if !NET5_0_OR_GREATER + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)] + internal sealed class MemberNotNullAttribute : Attribute + { + public MemberNotNullAttribute(string member) => Members = new string[] + { + member + }; + + public MemberNotNullAttribute(params string[] members) => Members = members; + + public string[] Members { get; } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new string[1] { member }; + } + + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + public bool ReturnValue { get; } + + public string[] Members { get; } + } +#endif +} diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props index 75dfc898b3..2861d6f2e2 100644 --- a/src/Microsoft.Data.SqlClient/tests/Directory.Build.props +++ b/src/Microsoft.Data.SqlClient/tests/Directory.Build.props @@ -16,7 +16,7 @@ - net461 + net462 netcoreapp3.1 diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs index 5c9c124763..aa042ffb65 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ConnectionStringBuilderShould.cs @@ -88,6 +88,17 @@ public void TestSqlConnectionStringAttestationProtocol() Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder3.AttestationProtocol); Assert.True(string.IsNullOrEmpty(connectionStringBuilder3.DataSource)); + + SqlConnectionStringBuilder connectionStringBuilder4 = new SqlConnectionStringBuilder(); + connectionStringBuilder4.AttestationProtocol = SqlConnectionAttestationProtocol.None; + Assert.Equal(SqlConnectionAttestationProtocol.None, connectionStringBuilder4.AttestationProtocol); + connectionStringBuilder4.DataSource = @"localhost"; + + VerifyAttestationProtocol(connectionStringBuilder4, SqlConnectionAttestationProtocol.None); + + connectionStringBuilder4.Clear(); + Assert.Equal(SqlConnectionAttestationProtocol.NotSpecified, connectionStringBuilder4.AttestationProtocol); + Assert.Empty(connectionStringBuilder4.DataSource); } [Fact] @@ -117,20 +128,32 @@ public void TestSqlConnectionStringBuilderEquivilantTo_AttestationProtocol() { SqlConnectionAttestationProtocol protocol1 = SqlConnectionAttestationProtocol.AAS; SqlConnectionAttestationProtocol protocol2 = SqlConnectionAttestationProtocol.HGS; + SqlConnectionAttestationProtocol protocol3 = SqlConnectionAttestationProtocol.None; SqlConnectionStringBuilder connectionStringBuilder1 = new SqlConnectionStringBuilder(); SqlConnectionStringBuilder connectionStringBuilder2 = new SqlConnectionStringBuilder(); + SqlConnectionStringBuilder connectionStringBuilder3 = new SqlConnectionStringBuilder(); // Modify the default value and set the same value on the both the builder objects above. connectionStringBuilder1.AttestationProtocol = protocol1; connectionStringBuilder2.AttestationProtocol = protocol1; + connectionStringBuilder3.AttestationProtocol = protocol1; // Use the EquivalentTo function to compare both the builder objects and make sure the result is expected. Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + Assert.True(connectionStringBuilder1.EquivalentTo(connectionStringBuilder3)); + Assert.Equal(connectionStringBuilder1.AttestationProtocol, connectionStringBuilder2.AttestationProtocol); + Assert.Equal(connectionStringBuilder1.AttestationProtocol, connectionStringBuilder3.AttestationProtocol); connectionStringBuilder2.AttestationProtocol = protocol2; - Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder2)); + Assert.True(!connectionStringBuilder3.EquivalentTo(connectionStringBuilder2)); + Assert.Equal(protocol2, connectionStringBuilder2.AttestationProtocol); + + connectionStringBuilder3.AttestationProtocol = protocol3; + Assert.True(!connectionStringBuilder1.EquivalentTo(connectionStringBuilder3)); + Assert.True(!connectionStringBuilder2.EquivalentTo(connectionStringBuilder3)); + Assert.Equal(protocol3, connectionStringBuilder3.AttestationProtocol); } @@ -151,14 +174,15 @@ public void TestSqlConnectionStringBuilderColumnEncryptionSetting(SqlConnectionC [InlineData(SqlConnectionAttestationProtocol.AAS)] [InlineData(SqlConnectionAttestationProtocol.HGS)] [InlineData(SqlConnectionAttestationProtocol.NotSpecified)] + [InlineData(SqlConnectionAttestationProtocol.None)] public void TestSqlConnectionStringBuilderAttestationProtocol(SqlConnectionAttestationProtocol protocol) { SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(); connectionStringBuilder.DataSource = @"localhost"; // Modify value. - connectionStringBuilder.AttestationProtocol = protocol; - + connectionStringBuilder.AttestationProtocol = protocol; + //Create a connection object with the above builder and verify the expected value. VerifyAttestationProtocol(connectionStringBuilder, protocol); } @@ -313,6 +337,12 @@ public void TestSqlConnectionStringBuilderTryGetValue(SqlConnectionColumnEncrypt tryGetValueResult = connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue); Assert.True(tryGetValueResult); Assert.Equal(SqlConnectionAttestationProtocol.AAS, outputValue); + + connectionStringBuilder.AttestationProtocol = SqlConnectionAttestationProtocol.None; + + Assert.True(connectionStringBuilder.TryGetValue(@"Attestation Protocol", out outputValue), + "'Attestation Protocol'' key not found in SqlConnectionStringBuilder"); + Assert.Equal(SqlConnectionAttestationProtocol.None, outputValue); } [Theory] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs index dfec766011..4c62013843 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionRegisterKeyStoreProvider.cs @@ -85,21 +85,24 @@ public void TestEmptyProviderName() [Fact] public void TestCanSetGlobalProvidersOnlyOnce() { - Utility.ClearSqlConnectionGlobalProviders(); + lock (Utility.ClearSqlConnectionGlobalProvidersLock) + { + Utility.ClearSqlConnectionGlobalProviders(); - IDictionary customProviders = - new Dictionary() - { + IDictionary customProviders = + new Dictionary() + { { DummyKeyStoreProvider.Name, new DummyKeyStoreProvider() } - }; - SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders); + }; + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders); - InvalidOperationException e = Assert.Throws( - () => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - string expectedMessage = SystemDataResourceManager.Instance.TCE_CanOnlyCallOnce; - Assert.Contains(expectedMessage, e.Message); + InvalidOperationException e = Assert.Throws( + () => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + string expectedMessage = SystemDataResourceManager.Instance.TCE_CanOnlyCallOnce; + Assert.Contains(expectedMessage, e.Message); - Utility.ClearSqlConnectionGlobalProviders(); + Utility.ClearSqlConnectionGlobalProviders(); + } } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionsAlgorithmErrors.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionsAlgorithmErrors.cs index 7395816fb1..e2d8e02b0b 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionsAlgorithmErrors.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/ExceptionsAlgorithmErrors.cs @@ -82,7 +82,7 @@ public void TestInvalidCipherText() [PlatformSpecific(TestPlatforms.Windows)] public void TestInvalidAlgorithmVersion() { - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidAlgorithmVersion, + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_InvalidAlgorithmVersion, 40, "01"); byte[] plainText = Encoding.Unicode.GetBytes("Hello World"); byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic); @@ -112,7 +112,7 @@ public void TestInvalidAuthenticationTag() [PlatformSpecific(TestPlatforms.Windows)] public void TestNullColumnEncryptionAlgorithm() { - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_NullColumnEncryptionAlgorithm, + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_NullColumnEncryptionAlgorithm, "'AEAD_AES_256_CBC_HMAC_SHA256'"); Object cipherMD = GetSqlCipherMetadata(0, 0, null, 1, 0x01); AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP"); @@ -148,24 +148,27 @@ public void TestUnknownEncryptionAlgorithmId() [PlatformSpecific(TestPlatforms.Windows)] public void TestUnknownCustomKeyStoreProvider() { - // Clear out the existing providers (to ensure test reliability) - ClearSqlConnectionGlobalProviders(); - - const string invalidProviderName = "Dummy_Provider"; - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnrecognizedKeyStoreProviderName, - invalidProviderName, "'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'", ""); - Object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x03); - AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, invalidProviderName, "RSA_OAEP"); - byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld"); - byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic); + lock (Utility.ClearSqlConnectionGlobalProvidersLock) + { + // Clear out the existing providers (to ensure test reliability) + ClearSqlConnectionGlobalProviders(); - Exception decryptEx = Assert.Throws(() => DecryptWithKey(plainText, cipherMD)); - Assert.Contains(expectedMessage, decryptEx.InnerException.Message); + const string invalidProviderName = "Dummy_Provider"; + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnrecognizedKeyStoreProviderName, + invalidProviderName, "'MSSQL_CERTIFICATE_STORE', 'MSSQL_CNG_STORE', 'MSSQL_CSP_PROVIDER'", ""); + Object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x03); + AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, invalidProviderName, "RSA_OAEP"); + byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld"); + byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic); - Exception encryptEx = Assert.Throws(() => EncryptWithKey(plainText, cipherMD)); - Assert.Contains(expectedMessage, encryptEx.InnerException.Message); + Exception decryptEx = Assert.Throws(() => DecryptWithKey(plainText, cipherMD)); + Assert.Contains(expectedMessage, decryptEx.InnerException.Message); + + Exception encryptEx = Assert.Throws(() => EncryptWithKey(plainText, cipherMD)); + Assert.Contains(expectedMessage, encryptEx.InnerException.Message); - ClearSqlConnectionGlobalProviders(); + ClearSqlConnectionGlobalProviders(); + } } [Fact] @@ -173,7 +176,7 @@ public void TestUnknownCustomKeyStoreProvider() public void TestTceUnknownEncryptionAlgorithm() { const string unknownEncryptionAlgorithm = "Dummy"; - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnknownColumnEncryptionAlgorithm, + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_UnknownColumnEncryptionAlgorithm, unknownEncryptionAlgorithm, "'AEAD_AES_256_CBC_HMAC_SHA256'"); Object cipherMD = GetSqlCipherMetadata(0, 0, "Dummy", 1, 0x01); AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "MSSQL_CERTIFICATE_STORE", "RSA_OAEP"); @@ -193,7 +196,7 @@ public void TestExceptionsFromCertStore() { byte[] corruptedCek = GenerateInvalidEncryptedCek(CertFixture.cek, ECEKCorruption.SIGNATURE); - string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_KeyDecryptionFailedCertStore, + string expectedMessage = string.Format(SystemDataResourceManager.Instance.TCE_KeyDecryptionFailedCertStore, "MSSQL_CERTIFICATE_STORE", BitConverter.ToString(corruptedCek, corruptedCek.Length - 10, 10)); Object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x01); @@ -209,27 +212,30 @@ public void TestExceptionsFromCertStore() [PlatformSpecific(TestPlatforms.Windows)] public void TestExceptionsFromCustomKeyStore() { - string expectedMessage = "Failed to decrypt a column encryption key"; + lock (Utility.ClearSqlConnectionGlobalProvidersLock) + { + string expectedMessage = "Failed to decrypt a column encryption key"; - // Clear out the existing providers (to ensure test reliability) - ClearSqlConnectionGlobalProviders(); + // Clear out the existing providers (to ensure test reliability) + ClearSqlConnectionGlobalProviders(); - IDictionary customProviders = new Dictionary(); - customProviders.Add(DummyKeyStoreProvider.Name, new DummyKeyStoreProvider()); - SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders); + IDictionary customProviders = new Dictionary(); + customProviders.Add(DummyKeyStoreProvider.Name, new DummyKeyStoreProvider()); + SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders); - object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x01); - AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "DummyProvider", "DummyAlgo"); - byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld"); - byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic); + object cipherMD = GetSqlCipherMetadata(0, 1, null, 1, 0x01); + AddEncryptionKeyToCipherMD(cipherMD, CertFixture.encryptedCek, 0, 0, 0, new byte[] { 0x01, 0x02, 0x03 }, CertFixture.certificatePath, "DummyProvider", "DummyAlgo"); + byte[] plainText = Encoding.Unicode.GetBytes("HelloWorld"); + byte[] cipherText = EncryptDataUsingAED(plainText, CertFixture.cek, CColumnEncryptionType.Deterministic); - Exception decryptEx = Assert.Throws(() => DecryptWithKey(cipherText, cipherMD)); - Assert.Contains(expectedMessage, decryptEx.InnerException.Message); + Exception decryptEx = Assert.Throws(() => DecryptWithKey(cipherText, cipherMD)); + Assert.Contains(expectedMessage, decryptEx.InnerException.Message); - Exception encryptEx = Assert.Throws(() => EncryptWithKey(cipherText, cipherMD)); - Assert.Contains(expectedMessage, encryptEx.InnerException.Message); + Exception encryptEx = Assert.Throws(() => EncryptWithKey(cipherText, cipherMD)); + Assert.Contains(expectedMessage, encryptEx.InnerException.Message); - ClearSqlConnectionGlobalProviders(); + ClearSqlConnectionGlobalProviders(); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs index 54dd6bc6be..b0c6297cda 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlColumnEncryptionCertificateStoreProviderShould.cs @@ -92,11 +92,6 @@ public class SqlColumnEncryptionCertificateStoreProviderWindowsShould : IClassFi /// private const int CipherTextStartIndex = IVStartIndex + IVLengthInBytes; - /// - /// SetCustomColumnEncryptionKeyStoreProvider can be called only once in a process. To workaround that, we use this flag. - /// - private static bool s_testCustomEncryptioKeyStoreProviderExecutedOnce = false; - [Theory] [InvalidDecryptionParameters] [PlatformSpecific(TestPlatforms.Windows)] @@ -326,55 +321,51 @@ public void TestAeadEncryptionReversal(string dataType, object data, Utility.CCo [PlatformSpecific(TestPlatforms.Windows)] public void TestCustomKeyProviderListSetter() { - // SqlConnection.RegisterColumnEncryptionKeyStoreProviders can be called only once in a process. - // This is a workaround to ensure re-runnability of the test. - if (s_testCustomEncryptioKeyStoreProviderExecutedOnce) + lock (Utility.ClearSqlConnectionGlobalProvidersLock) { - return; + string expectedMessage1 = "Column encryption key store provider dictionary cannot be null. Expecting a non-null value."; + // Verify that we are able to set it to null. + ArgumentException e1 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(null)); + Assert.Contains(expectedMessage1, e1.Message); + + // A dictionary holding custom providers. + IDictionary customProviders = new Dictionary(); + customProviders.Add(new KeyValuePair(@"DummyProvider", new DummyKeyStoreProvider())); + + // Verify that setting a provider in the list with null value throws an exception. + customProviders.Add(new KeyValuePair(@"CustomProvider", null)); + string expectedMessage2 = "Null reference specified for key store provider 'CustomProvider'. Expecting a non-null value."; + ArgumentNullException e2 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + Assert.Contains(expectedMessage2, e2.Message); + customProviders.Remove(@"CustomProvider"); + + // Verify that setting a provider in the list with an empty provider name throws an exception. + customProviders.Add(new KeyValuePair(@"", new DummyKeyStoreProvider())); + string expectedMessage3 = "Invalid key store provider name specified. Key store provider names cannot be null or empty"; + ArgumentNullException e3 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + Assert.Contains(expectedMessage3, e3.Message); + + customProviders.Remove(@""); + + // Verify that setting a provider in the list with name that starts with 'MSSQL_' throws an exception. + customProviders.Add(new KeyValuePair(@"MSSQL_MyStore", new SqlColumnEncryptionCertificateStoreProvider())); + string expectedMessage4 = "Invalid key store provider name 'MSSQL_MyStore'. 'MSSQL_' prefix is reserved for system key store providers."; + ArgumentException e4 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + Assert.Contains(expectedMessage4, e4.Message); + + customProviders.Remove(@"MSSQL_MyStore"); + + // Verify that setting a provider in the list with name that starts with 'MSSQL_' but different case throws an exception. + customProviders.Add(new KeyValuePair(@"MsSqL_MyStore", new SqlColumnEncryptionCertificateStoreProvider())); + string expectedMessage5 = "Invalid key store provider name 'MsSqL_MyStore'. 'MSSQL_' prefix is reserved for system key store providers."; + ArgumentException e5 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); + Assert.Contains(expectedMessage5, e5.Message); + + customProviders.Remove(@"MsSqL_MyStore"); + + // Clear any providers set by other tests. + Utility.ClearSqlConnectionGlobalProviders(); } - - string expectedMessage1 = "Column encryption key store provider dictionary cannot be null. Expecting a non-null value."; - // Verify that we are able to set it to null. - ArgumentException e1 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(null)); - Assert.Contains(expectedMessage1, e1.Message); - - // A dictionary holding custom providers. - IDictionary customProviders = new Dictionary(); - customProviders.Add(new KeyValuePair(@"DummyProvider", new DummyKeyStoreProvider())); - - // Verify that setting a provider in the list with null value throws an exception. - customProviders.Add(new KeyValuePair(@"CustomProvider", null)); - string expectedMessage2 = "Null reference specified for key store provider 'CustomProvider'. Expecting a non-null value."; - ArgumentNullException e2 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage2, e2.Message); - customProviders.Remove(@"CustomProvider"); - - // Verify that setting a provider in the list with an empty provider name throws an exception. - customProviders.Add(new KeyValuePair(@"", new DummyKeyStoreProvider())); - string expectedMessage3 = "Invalid key store provider name specified. Key store provider names cannot be null or empty"; - ArgumentNullException e3 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage3, e3.Message); - - customProviders.Remove(@""); - - // Verify that setting a provider in the list with name that starts with 'MSSQL_' throws an exception. - customProviders.Add(new KeyValuePair(@"MSSQL_MyStore", new SqlColumnEncryptionCertificateStoreProvider())); - string expectedMessage4 = "Invalid key store provider name 'MSSQL_MyStore'. 'MSSQL_' prefix is reserved for system key store providers."; - ArgumentException e4 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage4, e4.Message); - - customProviders.Remove(@"MSSQL_MyStore"); - - // Verify that setting a provider in the list with name that starts with 'MSSQL_' but different case throws an exception. - customProviders.Add(new KeyValuePair(@"MsSqL_MyStore", new SqlColumnEncryptionCertificateStoreProvider())); - string expectedMessage5 = "Invalid key store provider name 'MsSqL_MyStore'. 'MSSQL_' prefix is reserved for system key store providers."; - ArgumentException e5 = Assert.Throws(() => SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customProviders)); - Assert.Contains(expectedMessage5, e5.Message); - - customProviders.Remove(@"MsSqL_MyStore"); - - // Clear any providers set by other tests. - Utility.ClearSqlConnectionGlobalProviders(); } [Theory] @@ -502,7 +493,7 @@ public class CEKEncryptionReversalParameters : DataAttribute { public override IEnumerable GetData(MethodInfo testMethod) { - yield return new object[2] { StoreLocation.CurrentUser , CurrentUserMyPathPrefix }; + yield return new object[2] { StoreLocation.CurrentUser, CurrentUserMyPathPrefix }; // use localmachine cert path only when current user is Admin. if (CertificateFixture.IsAdmin) { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs index faa48c7763..1669b228f7 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/SqlConnectionShould.cs @@ -151,5 +151,16 @@ SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting_2 } } } + + [Fact] + public void TestInvalidAttestationProtocolInConnectionString() + { + string connectionString = "Data Source=localhost; Initial Catalog = testdb; Column Encryption Setting=Enabled;"; + ArgumentException ex = Assert.Throws(() => new SqlConnection(connectionString + "Attestation protocol=invalid")); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + Assert.Contains("Invalid value for key 'Attestation Protocol'", ex.Message); + Assert.Null(ex.ParamName); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/TestFixtures.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/TestFixtures.cs index 862dc40ec0..b5e920e1b0 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/TestFixtures.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/TestFixtures.cs @@ -34,7 +34,7 @@ public static string DefaultConnectionString(SqlConnectionColumnEncryptionSettin SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder(); csb.DataSource = "localhost,12345"; csb.Pooling = false; - csb.Encrypt = false; + csb.Encrypt = SqlConnectionEncryptOption.Optional; csb.ConnectTimeout = 65534; csb.UserID = "prodUser1@FedAuthAzureSqlDb.onmicrosoft.com"; csb.ColumnEncryptionSetting = columnEncryptionSetting; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs index 6bc51d6acd..d91a15dee8 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/AlwaysEncryptedTests/Utility.cs @@ -95,8 +95,10 @@ internal static byte[] GenerateRandomBytes(int length) { // Generate random bytes cryptographically. byte[] randomBytes = new byte[length]; - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } return randomBytes; } @@ -402,9 +404,11 @@ internal static string GetHexString(byte[] input, bool addLeadingZeroX = false) return str.ToString(); } + internal static object ClearSqlConnectionGlobalProvidersLock = new(); + /// /// Through reflection, clear the static provider list set on SqlConnection. - /// Note- This API doesn't use locks for synchronization. + /// Note- Any test using this method should be wrapped in a lock statement using ClearSqlConnectionGlobalProvidersLock /// internal static void ClearSqlConnectionGlobalProviders() { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj index 97336390d6..5b1ec82808 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj @@ -46,6 +46,7 @@ + @@ -96,6 +97,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlClientMetaDataCollectionNamesTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlClientMetaDataCollectionNamesTest.cs index 789949ee68..7c51a326c2 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlClientMetaDataCollectionNamesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlClientMetaDataCollectionNamesTest.cs @@ -25,6 +25,7 @@ public void ValuesTest() Assert.Equal("Views", SqlClientMetaDataCollectionNames.Views); Assert.Equal("AllColumns", SqlClientMetaDataCollectionNames.AllColumns); Assert.Equal("ColumnSetColumns", SqlClientMetaDataCollectionNames.ColumnSetColumns); + Assert.Equal("StructuredTypeMembers", SqlClientMetaDataCollectionNames.StructuredTypeMembers); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs index 0ec7a41973..039bf2f766 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionBasicTests.cs @@ -46,7 +46,7 @@ public void TransientFaultTest(uint errorCode) { DataSource = "localhost," + server.Port, IntegratedSecurity = true, - Encrypt = false + Encrypt = SqlConnectionEncryptOption.Optional }; using SqlConnection connection = new(builder.ConnectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs index d80875af80..828152c4ab 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionStringBuilderTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Xunit; namespace Microsoft.Data.SqlClient.Tests @@ -52,7 +53,9 @@ public partial class SqlConnectionStringBuilderTest [InlineData("Addr = randomserver.sys.local; User Id = a; Password = b")] [InlineData("Database = master")] [InlineData("Enclave Attestation Url = http://dymmyurl")] - [InlineData("Encrypt = true")] + [InlineData("Encrypt = True")] + [InlineData("Encrypt = False")] + [InlineData("Encrypt = Strict")] [InlineData("Enlist = false")] [InlineData("Initial Catalog = Northwind; Failover Partner = randomserver.sys.local")] [InlineData("Initial Catalog = tempdb")] @@ -82,6 +85,12 @@ public partial class SqlConnectionStringBuilderTest [InlineData("User Instance = true")] [InlineData("Workstation ID = myworkstation")] [InlineData("WSID = myworkstation")] + [InlineData("Host Name In Certificate = tds.test.com")] + [InlineData("HostNameInCertificate = tds.test.com")] + [InlineData("Server SPN = server1")] + [InlineData("ServerSPN = server2")] + [InlineData("Failover Partner SPN = server3")] + [InlineData("FailoverPartnerSPN = server4")] public void ConnectionStringTests(string connectionString) { ExecuteConnectionStringTests(connectionString); @@ -307,6 +316,116 @@ public void ConnectionStringBuilder_AttachDbFileName_DataDirectory(string value, Assert.Equal(expected, builder.ConnectionString); } + [Fact] + public void SetEncryptInConnectionStringMapsToString() + { + var data = new List> + { + Tuple.Create("Encrypt=yes", SqlConnectionEncryptOption.Mandatory), + Tuple.Create("Encrypt=no", SqlConnectionEncryptOption.Optional), + Tuple.Create("Encrypt=true", SqlConnectionEncryptOption.Mandatory), + Tuple.Create("Encrypt=false", SqlConnectionEncryptOption.Optional), + Tuple.Create("Encrypt=mandatory", SqlConnectionEncryptOption.Mandatory), + Tuple.Create("Encrypt=optional", SqlConnectionEncryptOption.Optional), + Tuple.Create("Encrypt=strict", SqlConnectionEncryptOption.Strict) + }; + + foreach (var item in data) + { + string connectionString = item.Item1; + SqlConnectionEncryptOption expected = item.Item2; + SqlConnection sqlConnection = new(connectionString); + SqlConnectionStringBuilder scsb = new(sqlConnection.ConnectionString); + Assert.Equal(expected, scsb.Encrypt); + } + } + + [Fact] + public void SetEncryptOnConnectionBuilderMapsToString() + { + var data = new List> + { + Tuple.Create("Encrypt=True", SqlConnectionEncryptOption.Mandatory), + Tuple.Create("Encrypt=False", SqlConnectionEncryptOption.Optional), + Tuple.Create("Encrypt=Strict", SqlConnectionEncryptOption.Strict) + }; + + foreach (Tuple item in data) + { + string expected = item.Item1; + SqlConnectionEncryptOption option = item.Item2; + SqlConnectionStringBuilder scsb = new(); + scsb.Encrypt = option; + Assert.Equal(expected, scsb.ConnectionString); + } + } + + [Fact] + public void ConnectionBuilderEncryptBackwardsCompatibility() + { + SqlConnectionStringBuilder builder = new(); + builder.Encrypt = false; + Assert.Equal("Encrypt=False", builder.ConnectionString); + Assert.False(builder.Encrypt); + + builder.Encrypt = true; + Assert.Equal("Encrypt=True", builder.ConnectionString); + Assert.True(builder.Encrypt); + + builder.Encrypt = SqlConnectionEncryptOption.Optional; + Assert.Equal("Encrypt=False", builder.ConnectionString); + Assert.False(builder.Encrypt); + + builder.Encrypt = SqlConnectionEncryptOption.Mandatory; + Assert.Equal("Encrypt=True", builder.ConnectionString); + Assert.True(builder.Encrypt); + + builder.Encrypt = SqlConnectionEncryptOption.Strict; + Assert.Equal("Encrypt=Strict", builder.ConnectionString); + Assert.True(builder.Encrypt); + } + + [Theory] + [InlineData("true", "True")] + [InlineData("mandatory", "True")] + [InlineData("yes", "True")] + [InlineData("false", "False")] + [InlineData("optional", "False")] + [InlineData("no", "False")] + [InlineData("strict", "Strict")] + public void EncryptParserValidValuesParsesSuccessfully(string value, string expectedValue) + => Assert.Equal(expectedValue, SqlConnectionEncryptOption.Parse(value).ToString()); + + [Theory] + [InlineData("something")] + [InlineData("")] + [InlineData(null)] + [InlineData(" true ")] + public void EncryptParserInvalidValuesThrowsException(string value) + => Assert.Throws(() => SqlConnectionEncryptOption.Parse(value)); + + [Theory] + [InlineData("true", "True")] + [InlineData("mandatory", "True")] + [InlineData("yes", "True")] + [InlineData("false", "False")] + [InlineData("optional", "False")] + [InlineData("no", "False")] + [InlineData("strict", "Strict")] + public void EncryptTryParseValidValuesReturnsTrue(string value, string expectedValue) + { + Assert.True(SqlConnectionEncryptOption.TryParse(value, out var result)); + Assert.Equal(expectedValue, result.ToString()); + } + + [Theory] + [InlineData("something")] + [InlineData("")] + [InlineData(null)] + [InlineData(" true ")] + public void EncryptTryParseInvalidValuesReturnsFalse(string value) + => Assert.False(SqlConnectionEncryptOption.TryParse(value, out _)); + internal void ExecuteConnectionStringTests(string connectionString) { SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs index 888abca555..1ce20f9735 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConnectionTest.cs @@ -6,6 +6,7 @@ using System.Data; using System.Collections.Generic; using Xunit; +using System.Reflection; namespace Microsoft.Data.SqlClient.Tests { @@ -872,28 +873,35 @@ public void ConnectionString_UserInstance_Invalid() Assert.Null(ex.ParamName); } - [Fact] - public void ConnectionString_OtherKeywords() + [Theory] + [InlineData("Application Name=test")] + [InlineData("App=test")] + // [InlineData("Connection Reset=true")] // see https://github.com/dotnet/SqlClient/issues/17 + [InlineData("Current Language=test")] + [InlineData("Language=test")] + [InlineData("Encrypt=false")] + [InlineData("Encrypt=true")] + [InlineData("Encrypt=yes")] + [InlineData("Encrypt=no")] + [InlineData("Encrypt=strict")] + [InlineData("Encrypt=mandatory")] + [InlineData("Encrypt=optional")] + [InlineData("Host Name In Certificate=tds.test.com")] + [InlineData("HostNameInCertificate=tds.test.com")] + [InlineData("Enlist=false")] + [InlineData("Enlist=true")] + [InlineData("Integrated Security=true")] + [InlineData("Trusted_connection=true")] + [InlineData("Max Pool Size=10")] + [InlineData("Min Pool Size=10")] + [InlineData("Pooling=true")] + [InlineData("attachdbfilename=dunno")] + [InlineData("extended properties=dunno")] + [InlineData("initial file name=dunno")] + public void ConnectionString_OtherKeywords(string connectionString) { SqlConnection cn = new SqlConnection(); - cn.ConnectionString = "Application Name=test"; - cn.ConnectionString = "App=test"; - // see https://github.com/dotnet/SqlClient/issues/17 - //cn.ConnectionString = "Connection Reset=true"; - cn.ConnectionString = "Current Language=test"; - cn.ConnectionString = "Language=test"; - cn.ConnectionString = "Encrypt=false"; - cn.ConnectionString = "Encrypt=true"; - cn.ConnectionString = "Enlist=false"; - cn.ConnectionString = "Enlist=true"; - cn.ConnectionString = "Integrated Security=true"; - cn.ConnectionString = "Trusted_connection=true"; - cn.ConnectionString = "Max Pool Size=10"; - cn.ConnectionString = "Min Pool Size=10"; - cn.ConnectionString = "Pooling=true"; - cn.ConnectionString = "attachdbfilename=dunno"; - cn.ConnectionString = "extended properties=dunno"; - cn.ConnectionString = "initial file name=dunno"; + cn.ConnectionString = connectionString; } [Fact] @@ -1031,5 +1039,44 @@ public void ConnectionString_IPAddressPreference_Invalid(string value) Assert.Contains("'ip address preference'", ex.Message, StringComparison.OrdinalIgnoreCase); Assert.Null(ex.ParamName); } + + [Theory] + [InlineData("Server SPN = server1")] + [InlineData("ServerSPN = server2")] + [InlineData("Failover Partner SPN = server3")] + [InlineData("FailoverPartnerSPN = server4")] + public void ConnectionString_ServerSPN_FailoverPartnerSPN(string value) + { + SqlConnection _ = new(value); + } + + [Fact] + public void ConnectionRetryForNonAzureEndpoints() + { + SqlConnection cn = new SqlConnection("Data Source = someserver"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(1, (int)field.GetValue(cn)); + } + + [Fact] + public void ConnectionRetryForAzureDbEndpoints() + { + SqlConnection cn = new SqlConnection("Data Source = someserver.database.windows.net"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(2, (int)field.GetValue(cn)); + } + + [Theory] + [InlineData("myserver-ondemand.sql.azuresynapse.net")] + [InlineData("someserver-ondemand.database.windows.net")] + public void ConnectionRetryForAzureOnDemandEndpoints(string serverName) + { + SqlConnection cn = new SqlConnection($"Data Source = {serverName}"); + FieldInfo field = typeof(SqlConnection).GetField("_connectRetryCount", BindingFlags.Instance | BindingFlags.NonPublic); + Assert.NotNull(field.GetValue(cn)); + Assert.Equal(5, (int)field.GetValue(cn)); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs index 56f95b9962..77546de525 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs @@ -390,9 +390,6 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } - [SqlUserDefinedType(Format.UserDefined)] - public class TestUdt - { - - } + [SqlServer.Server.SqlUserDefinedType(SqlServer.Server.Format.UserDefined)] + public class TestUdt {} } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs new file mode 100644 index 0000000000..13b78449f4 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using Xunit; + +namespace Microsoft.Data.SqlClient.Tests +{ + public class SqlErrorTest + { + private const string SQLMSF_FailoverPartnerNotSupported = + "Connecting to a mirrored SQL Server instance using the MultiSubnetFailover connection option is not supported."; + private const byte FATAL_ERROR_CLASS = 20; + +#if !NET50_OR_LATER + [Fact] + public static void SqlErrorSerializationTest() + { + DataContractSerializer serializer = new DataContractSerializer(typeof(SqlError)); + SqlError expected = CreateError(); + SqlError actual = null; + using (var stream = new MemoryStream()) + { + try + { + serializer.WriteObject(stream, expected); + stream.Position = 0; + actual = (SqlError)serializer.ReadObject(stream); + } + catch (Exception ex) + { + Assert.False(true, $"Unexpected Exception occurred: {ex.Message}"); + } + } + + Assert.Equal(expected.Message, actual.Message); + Assert.Equal(expected.Number, actual.Number); + Assert.Equal(expected.State, actual.State); + Assert.Equal(expected.Class, actual.Class); + Assert.Equal(expected.Server, actual.Server); + Assert.Equal(expected.Procedure, actual.Procedure); + Assert.Equal(expected.LineNumber, actual.LineNumber); + Assert.Equal(expected.Source, actual.Source); + } +#endif + + + private static SqlError CreateError() + { + string msg = SQLMSF_FailoverPartnerNotSupported; + + Type sqlErrorType = typeof(SqlError); + + // SqlError only has internal constructors, in order to instantiate this, we use reflection + SqlError sqlError = (SqlError)sqlErrorType.Assembly.CreateInstance( + sqlErrorType.FullName, + false, + BindingFlags.Instance | BindingFlags.NonPublic, + null, + new object[] { 100, (byte)0x00, FATAL_ERROR_CLASS, "ServerName", msg, "ProcedureName", 10, null }, + null, + null); + + return sqlError; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs index 7e74fba8f5..d57cf853e9 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; using Xunit; namespace Microsoft.Data.SqlClient.Tests diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs index d2306431f4..a9123e1582 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlMetaDataTest.cs @@ -511,6 +511,11 @@ public void DecimalConstructorWithPrecisionOutofRange2_Throws() [InlineData(SqlDbType.Udt, typeof(Address))] public void GenericConstructorWithoutXmlSchema(SqlDbType dbType, Type udt) { + if (udt != null) + { + Type t = udt.GetInterface("IBinarySerialize", true); + Assert.Equal(typeof(Microsoft.SqlServer.Server.IBinarySerialize), t); + } SqlMetaData metaData = new SqlMetaData("col2", dbType, 16, 2, 2, 2, SqlCompareOptions.IgnoreCase, udt, true, true, SortOrder.Ascending, 0); Assert.Equal(dbType, metaData.SqlDbType); Assert.True(metaData.UseServerDefault); @@ -712,6 +717,15 @@ public void UdtConstructorTest() Assert.Equal(-1, metaData.SortOrdinal); } + [Fact] + public static void InvalidUdtEcxeption_Throws() + { + SqlServer.Server.InvalidUdtException e = + Assert.Throws (() => new SqlMetaData("col1", SqlDbType.Udt, typeof(int), "UdtTestDb.dbo.Address")); + + Assert.Equal("'System.Int32' is an invalid user defined type, reason: no UDT attribute.", e.Message); + } + [Fact] public void UdtConstructorTestWithoutServerTypeName() { diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterCollectionTest.cs index e93dbcc196..76043dc350 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterCollectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlParameterCollectionTest.cs @@ -110,5 +110,15 @@ public void CollectionValiateNull_Throws() Assert.Throws(() => collection.Add(null)); } + + [Fact] + public void CollectionAddInvalidType_Throws() + { + SqlCommand command = new SqlCommand(); + SqlParameterCollection collection = command.Parameters; + + InvalidCastException ex = Assert.Throws(() => collection.Add(new SqlCommand())); + Assert.Contains("Microsoft.Data.SqlClient.SqlCommand", ex.Message, StringComparison.OrdinalIgnoreCase); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs index 3df270718f..51e2330bf0 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TestTdsServer.cs @@ -45,7 +45,7 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool server._endpoint.Start(); int port = server._endpoint.ServerEndPoint.Port; - server._connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = false }; + server._connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = connectionTimeout, Encrypt = SqlConnectionEncryptOption.Optional }; server.ConnectionString = server._connectionStringBuilder.ConnectionString; return server; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 2bec916210..5f60faddb5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -2122,6 +2122,24 @@ public void TestSqlCommandCancellationToken(string connection, int initalValue, } } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsSGXEnclaveConnStringSetup))] + public void TestNoneAttestationProtocolWithSGXEnclave() + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionStringAASSGX); + builder.AttestationProtocol = SqlConnectionAttestationProtocol.None; + builder.EnclaveAttestationUrl = string.Empty; + + using (SqlConnection connection = new(builder.ConnectionString)) + { + InvalidOperationException ex = Assert.Throws(() => connection.Open()); + string expectedErrorMessage = string.Format( + SystemDataResourceManager.Instance.TCE_AttestationProtocolNotSupportEnclaveType, + SqlConnectionAttestationProtocol.None.ToString(), "SGX"); + Assert.Contains(expectedErrorMessage, ex.Message); + } + } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProvider))] public void TestConnectionCustomKeyStoreProviderDuringAeQuery(string connectionString) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs index dec4b42659..387f364f56 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/CspProviderExt.cs @@ -23,7 +23,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted [PlatformSpecific(TestPlatforms.Windows)] public class CspProviderExt { - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringSetupForAE))] [ClassData(typeof(AEConnectionStringProvider))] public void TestKeysFromCertificatesCreatedWithMultipleCryptoProviders(string connectionString) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs index 389efad48d..6cb20a4351 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ExceptionTestAKVStore.cs @@ -218,22 +218,24 @@ public void AkvStoreProviderVerifyFunctionWithInvalidSignature(bool fEnclaveEnab Buffer.BlockCopy(cmkSignature, 0, tamperedCmkSignature, 0, tamperedCmkSignature.Length); // Corrupt one byte at a time 10 times - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - byte[] randomIndexInCipherText = new byte[1]; - for (int i = 0; i < 10; i++) + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { - Assert.True(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature), @"tamperedCmkSignature before tampering should be verified without any problems."); + byte[] randomIndexInCipherText = new byte[1]; + for (int i = 0; i < 10; i++) + { + Assert.True(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature), @"tamperedCmkSignature before tampering should be verified without any problems."); - int startingByteIndex = 0; - rng.GetBytes(randomIndexInCipherText); + int startingByteIndex = 0; + rng.GetBytes(randomIndexInCipherText); - tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = (byte)(cmkSignature[startingByteIndex + randomIndexInCipherText[0]] + 1); + tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = (byte)(cmkSignature[startingByteIndex + randomIndexInCipherText[0]] + 1); - // Expect failed verification for invalid signature bytes - Assert.False(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature)); + // Expect failed verification for invalid signature bytes + Assert.False(fixture.AkvStoreProvider.VerifyColumnMasterKeyMetadata(DataTestUtility.AKVUrl, allowEnclaveComputations: fEnclaveEnabled, signature: tamperedCmkSignature)); - // Fix up the corrupted byte - tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = cmkSignature[startingByteIndex + randomIndexInCipherText[0]]; + // Fix up the corrupted byte + tamperedCmkSignature[startingByteIndex + randomIndexInCipherText[0]] = cmkSignature[startingByteIndex + randomIndexInCipherText[0]]; + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs index cd39d56b3c..9cea6b8239 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/DatabaseHelper.cs @@ -174,8 +174,10 @@ internal static byte[] GenerateRandomBytes(int length) { // Generate random bytes cryptographically. byte[] randomBytes = new byte[length]; - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } return randomBytes; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs index 3a995dd9f8..0cd8436cb4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/SQLSetupStrategy.cs @@ -137,6 +137,7 @@ protected List CreateTables(IList columnEncryptionKe SqlNullValuesTable = new SqlNullValuesTable(GenerateUniqueName("SqlNullValuesTable"), columnEncryptionKeys[0]); tables.Add(SqlNullValuesTable); + // columnEncryptionKeys[2] is encrypted with DummyCMK. use this encrypted column to test custom key store providers CustomKeyStoreProviderTestTable = new ApiTestTable(GenerateUniqueName("CustomKeyStoreProviderTestTable"), columnEncryptionKeys[2], columnEncryptionKeys[0], useDeterministicEncryption: true); tables.Add(CustomKeyStoreProviderTestTable); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnEncryptionKey.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnEncryptionKey.cs index 19a2d2149c..521328bad5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnEncryptionKey.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/TestFixtures/Setup/ColumnEncryptionKey.cs @@ -60,9 +60,11 @@ public static byte[] GenerateRandomBytes(int length) { // Generate random bytes cryptographically. byte[] randomBytes = new byte[length]; - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); - + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } + return randomBytes; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 27acea362d..9dd199da33 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -15,9 +15,11 @@ using System.Security; using System.Threading; using System.Threading.Tasks; -using Microsoft.Identity.Client; using Microsoft.Data.SqlClient.TestUtilities; +using Microsoft.Identity.Client; using Xunit; +using System.Net.NetworkInformation; +using System.Text; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -27,6 +29,7 @@ public static class DataTestUtility public static readonly string TCPConnectionString = null; public static readonly string TCPConnectionStringHGSVBS = null; public static readonly string TCPConnectionStringAASVBS = null; + public static readonly string TCPConnectionStringNoneVBS = null; public static readonly string TCPConnectionStringAASSGX = null; public static readonly string AADAuthorityURL = null; public static readonly string AADPasswordConnectionString = null; @@ -86,6 +89,7 @@ static DataTestUtility() TCPConnectionString = c.TCPConnectionString; TCPConnectionStringHGSVBS = c.TCPConnectionStringHGSVBS; TCPConnectionStringAASVBS = c.TCPConnectionStringAASVBS; + TCPConnectionStringNoneVBS = c.TCPConnectionStringNoneVBS; TCPConnectionStringAASSGX = c.TCPConnectionStringAASSGX; AADAuthorityURL = c.AADAuthorityURL; AADPasswordConnectionString = c.AADPasswordConnectionString; @@ -148,6 +152,11 @@ static DataTestUtility() AEConnStrings.Add(TCPConnectionStringAASVBS); } + if (!string.IsNullOrEmpty(TCPConnectionStringNoneVBS)) + { + AEConnStrings.Add(TCPConnectionStringNoneVBS); + } + if (!string.IsNullOrEmpty(TCPConnectionStringAASSGX)) { AEConnStrings.Add(TCPConnectionStringAASSGX); @@ -289,6 +298,11 @@ public static bool AreConnStringsSetup() return !string.IsNullOrEmpty(NPConnectionString) && !string.IsNullOrEmpty(TCPConnectionString); } + public static bool IsTCPConnStringSetup() + { + return !string.IsNullOrEmpty(TCPConnectionString); + } + // Synapse: Always Encrypted is not supported with Azure Synapse. // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/17858869-support-always-encrypted-in-sql-data-warehouse public static bool AreConnStringSetupForAE() @@ -296,6 +310,8 @@ public static bool AreConnStringSetupForAE() return AEConnStrings.Count > 0 && IsNotAzureSynapse(); } + public static bool IsSGXEnclaveConnStringSetup() => !string.IsNullOrEmpty(TCPConnectionStringAASSGX); + public static bool IsAADPasswordConnStrSetup() { return !string.IsNullOrEmpty(AADPasswordConnectionString); @@ -421,6 +437,7 @@ public static string GetUniqueNameForSqlServer(string prefix, bool withBracket = public static void DropTable(SqlConnection sqlConnection, string tableName) { + ResurrectConnection(sqlConnection); using (SqlCommand cmd = new SqlCommand(string.Format("IF (OBJECT_ID('{0}') IS NOT NULL) \n DROP TABLE {0}", tableName), sqlConnection)) { cmd.ExecuteNonQuery(); @@ -429,6 +446,7 @@ public static void DropTable(SqlConnection sqlConnection, string tableName) public static void DropUserDefinedType(SqlConnection sqlConnection, string typeName) { + ResurrectConnection(sqlConnection); using (SqlCommand cmd = new SqlCommand(string.Format("IF (TYPE_ID('{0}') IS NOT NULL) \n DROP TYPE {0}", typeName), sqlConnection)) { cmd.ExecuteNonQuery(); @@ -437,12 +455,25 @@ public static void DropUserDefinedType(SqlConnection sqlConnection, string typeN public static void DropStoredProcedure(SqlConnection sqlConnection, string spName) { + ResurrectConnection(sqlConnection); using (SqlCommand cmd = new SqlCommand(string.Format("IF (OBJECT_ID('{0}') IS NOT NULL) \n DROP PROCEDURE {0}", spName), sqlConnection)) { cmd.ExecuteNonQuery(); } } + private static void ResurrectConnection(SqlConnection sqlConnection, int counter = 2) + { + if (sqlConnection.State == ConnectionState.Closed) + { + sqlConnection.Open(); + } + while (counter-- > 0 && sqlConnection.State == ConnectionState.Connecting) + { + Thread.Sleep(80); + } + } + /// /// Drops specified database on provided connection. /// @@ -450,13 +481,13 @@ public static void DropStoredProcedure(SqlConnection sqlConnection, string spNam /// Database name without brackets. public static void DropDatabase(SqlConnection sqlConnection, string dbName) { + ResurrectConnection(sqlConnection); using SqlCommand cmd = new(string.Format("IF (EXISTS(SELECT 1 FROM sys.databases WHERE name = '{0}')) \nBEGIN \n ALTER DATABASE [{0}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE \n DROP DATABASE [{0}] \nEND", dbName), sqlConnection); cmd.ExecuteNonQuery(); } public static bool IsLocalDBInstalled() => !string.IsNullOrEmpty(LocalDbAppName?.Trim()) && IsIntegratedSecuritySetup(); public static bool IsLocalDbSharedInstanceSetup() => !string.IsNullOrEmpty(LocalDbSharedInstanceName?.Trim()) && IsIntegratedSecuritySetup(); - public static bool IsIntegratedSecuritySetup() => SupportsIntegratedSecurity; public static string GetAccessToken() @@ -504,7 +535,7 @@ public static string GetUserIdentityAccessToken() public static bool IsUserIdentityTokenSetup() => !string.IsNullOrEmpty(GetUserIdentityAccessToken()); - public static bool IsFileStreamSetup() => !string.IsNullOrEmpty(FileStreamDirectory); + public static bool IsFileStreamSetup() => !string.IsNullOrEmpty(FileStreamDirectory) && IsNotAzureServer() && IsNotAzureSynapse(); private static bool CheckException(Exception ex, string exceptionMessage, bool innerExceptionMustBeNull) where TException : Exception { @@ -820,6 +851,40 @@ public static string RetrieveValueFromConnStr(string connStr, string[] keywords) return res; } + public static bool ParseDataSource(string dataSource, out string hostname, out int port, out string instanceName) + { + hostname = string.Empty; + port = -1; + instanceName = string.Empty; + + if (dataSource.Contains(",") && dataSource.Contains("\\")) + return false; + + if (dataSource.Contains(":")) + { + dataSource = dataSource.Substring(dataSource.IndexOf(":") + 1); + } + + if (dataSource.Contains(",")) + { + if (!Int32.TryParse(dataSource.Substring(dataSource.LastIndexOf(",") + 1), out port)) + { + return false; + } + dataSource = dataSource.Substring(0, dataSource.IndexOf(",") - 1); + } + + if (dataSource.Contains("\\")) + { + instanceName = dataSource.Substring(dataSource.LastIndexOf("\\") + 1); + dataSource = dataSource.Substring(0, dataSource.LastIndexOf("\\")); + } + + hostname = dataSource; + + return hostname.Length > 0 && hostname.IndexOfAny(new char[] { '\\', ':', ',' }) == -1; + } + public class AKVEventListener : BaseEventListener { public override string Name => AKVEventSourceName; @@ -859,5 +924,22 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) } } } + + /// + /// Resolves the machine's fully qualified domain name if it is applicable. + /// + /// Returns FQDN if the client was domain joined otherwise the machine name. + public static string GetMachineFQDN() + { + IPGlobalProperties machineInfo = IPGlobalProperties.GetIPGlobalProperties(); + StringBuilder fqdn = new(); + fqdn.Append(machineInfo.HostName); + if (!string.IsNullOrEmpty(machineInfo.DomainName)) + { + fqdn.Append("."); + fqdn.Append(machineInfo.DomainName); + } + return fqdn.ToString(); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj index 3364313106..704d4a28f0 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj @@ -29,7 +29,7 @@ TCECryptoNativeBaselineRsa.txt - + @@ -159,6 +159,9 @@ + + + @@ -239,7 +242,7 @@ - + @@ -269,6 +272,7 @@ + @@ -298,10 +302,12 @@ + + @@ -316,8 +322,8 @@ - - + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs index 41a6404a20..cfe74831f7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectionPoolTest/ConnectionPoolTest.cs @@ -185,7 +185,11 @@ public static void MaxPoolWaitForConnectionTest(string connectionString) ManualResetEventSlim taskAllowedToSpeak = new ManualResetEventSlim(false); Task waitTask = Task.Factory.StartNew(() => MaxPoolWaitForConnectionTask(newConnectionString, internalConnection, connectionPool, taskAllowedToSpeak)); - Thread.Sleep(200); + int count = 5; + while (waitTask.Status == TaskStatus.WaitingToRun && count-- > 0) + { + Thread.Sleep(200); + } Assert.Equal(TaskStatus.Running, waitTask.Status); connection1.Close(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs index a05d485dff..b236ddfec5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/AADConnectionTest.cs @@ -691,7 +691,7 @@ public static void Azure_UserManagedIdentityTest() } } - [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure))] + [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup))] public static void Azure_AccessToken_SystemManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; @@ -705,7 +705,7 @@ public static void Azure_AccessToken_SystemManagedIdentityTest() } } - [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure))] + [ConditionalFact(nameof(AreConnStringsSetup), nameof(IsAzure), nameof(IsAccessTokenSetup))] public static void Azure_AccessToken_UserManagedIdentityTest() { string[] removeKeys = { "Authentication", "User ID", "Password", "UID", "PWD", "Trusted_Connection", "Integrated Security" }; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index 41a52f48ac..08113cbd3a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -5,7 +5,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Principal; using System.Threading; +using Microsoft.Win32; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -85,6 +88,21 @@ public static void EnvironmentHostNameSPIDTest() Assert.True(false, "No non-empty hostname found for the application"); } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static async void ConnectionTimeoutInfiniteTest() + { + // Exercise the special-case infinite connect timeout code path + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) + { + ConnectTimeout = 0 // Infinite + }; + + using SqlConnection conn = new(builder.ConnectionString); + CancellationTokenSource cts = new(30000); + // Will throw TaskCanceledException and fail the test in the event of a hang + await conn.OpenAsync(cts.Token); + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void ConnectionTimeoutTestWithThread() { @@ -368,5 +386,76 @@ public static void ConnectionOpenDisableRetry() duration = timer.Elapsed; Assert.True(duration.Seconds > 5, $"Connection Open() with retries took less time than expected. Expect > 5 sec with transient fault handling. Took {duration.Seconds} sec."); // sqlConnection.Open(); } + + private const string ConnectToPath = "SOFTWARE\\Microsoft\\MSSQLServer\\Client\\ConnectTo"; + private static bool CanCreateAliases() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || + !DataTestUtility.IsTCPConnStringSetup()) + { + return false; + } + + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) + { + WindowsPrincipal principal = new(identity); + if (!principal.IsInRole(WindowsBuiltInRole.Administrator)) + { + return false; + } + } + + using RegistryKey key = Registry.LocalMachine.OpenSubKey(ConnectToPath, true); + if (key == null) + { + // Registry not writable + return false; + } + + SqlConnectionStringBuilder b = new(DataTestUtility.TCPConnectionString); + if (!DataTestUtility.ParseDataSource(b.DataSource, out string hostname, out int port, out string instanceName) || + !string.IsNullOrEmpty(instanceName)) + { + return false; + } + + return true; + } + + [PlatformSpecific(TestPlatforms.Windows)] + [ConditionalFact(nameof(CanCreateAliases))] + public static void ConnectionAliasTest() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + throw new Exception("Alias test only valid on Windows"); + } + + if (!CanCreateAliases()) + { + throw new Exception("Unable to create aliases in this environment. Windows + Admin + non-instance data source required."); + } + + SqlConnectionStringBuilder b = new(DataTestUtility.TCPConnectionString); + if (!DataTestUtility.ParseDataSource(b.DataSource, out string hostname, out int port, out string instanceName) || + !string.IsNullOrEmpty(instanceName)) + { + // Only works with connection strings that parse successfully and don't include an instance name + throw new Exception("Unable to create aliases in this configuration. Parsable data source without instance required."); + } + + b.DataSource = "TESTALIAS-" + Guid.NewGuid().ToString().Replace("-", ""); + using RegistryKey key = Registry.LocalMachine.OpenSubKey(ConnectToPath, true); + key.SetValue(b.DataSource, "DBMSSOCN," + hostname + "," + (port == -1 ? 1433 : port)); + try + { + using SqlConnection sqlConnection = new(b.ConnectionString); + sqlConnection.Open(); + } + finally + { + key.DeleteValue(b.DataSource); + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs index 7b2742e8d6..283ddc5c63 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs @@ -64,7 +64,7 @@ public static void GetAllColumnsFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.AllColumns, new string[] { "IS_NULLABLE", "COLUMN_DEFAULT", "IS_FILESTREAM", "IS_SPARSE", "IS_COLUMN_SET" }); } - + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static void GetColumnSetColumnsFromSchema() { @@ -93,7 +93,13 @@ public static void GetViewColumnsFromSchema() public static void GetUserDefinedTypesFromSchema() { VerifySchemaTable(SqlClientMetaDataCollectionNames.UserDefinedTypes, new string[] { "assembly_name", "version_revision", "culture_info" }); - } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + public static void GetStructuredTypeMembersFromSchema() + { + VerifySchemaTable(SqlClientMetaDataCollectionNames.StructuredTypeMembers, new string[] { "TYPE_CATALOG", "TYPE_SCHEMA", "TYPE_NAME", "MEMBER_NAME", "ORDINAL_POSITION" }); + } private static void VerifySchemaTable(string schemaItemName, string[] testColumnNames) { @@ -104,11 +110,11 @@ private static void VerifySchemaTable(string schemaItemName, string[] testColumn using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) { - // Connect to the database then retrieve the schema information. + // Connect to the database then retrieve the schema information connection.Open(); DataTable table = connection.GetSchema(schemaItemName); - // Get all table columns + // Get all table columns HashSet columnNames = new HashSet(); foreach (DataColumn column in table.Columns) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs index 8c8b2bc1d4..385c0341cd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs @@ -51,7 +51,7 @@ public static async Task GetFieldValueAsync_OfStream(CommandBehavior behavior, b Assert.Equal(originalData, outputData); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(GetCommandBehavioursAndIsAsync))] public static async Task GetFieldValueAsync_OfXmlReader(CommandBehavior behavior, bool isExecuteAsync) { @@ -220,7 +220,7 @@ public static async void GetFieldValue_OfStream(CommandBehavior behavior, bool i Assert.Equal(originalData, outputData); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(GetCommandBehavioursAndIsAsync))] public static async void GetFieldValue_OfTextReader(CommandBehavior behavior, bool isExecuteAsync) { @@ -290,7 +290,7 @@ public static async void GetStream(CommandBehavior behavior, bool isExecuteAsync Assert.Equal(originalData, outputData); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(GetCommandBehavioursAndIsAsync))] public static async void GetXmlReader(CommandBehavior behavior, bool isExecuteAsync) { @@ -358,7 +358,7 @@ public static async void GetTextReader(CommandBehavior behavior, bool isExecuteA Assert.Equal(originalText, outputText); } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(GetCommandBehaviourAndAccessorTypes))] public static void NullStreamProperties(CommandBehavior behavior, AccessorType accessorType) { @@ -443,7 +443,7 @@ public static void NullStreamProperties(CommandBehavior behavior, AccessorType a } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [MemberData(nameof(GetCommandBehaviourAndAccessorTypes))] public static void InvalidCastExceptionStream(CommandBehavior behavior, AccessorType accessorType) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 81ccb30721..b8731ea672 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -282,6 +282,14 @@ public static void CheckNullRowVersionIsBDNull() Assert.IsType(result); Assert.Equal(result, reader.GetFieldValue(0)); Assert.Throws(() => reader.GetFieldValue(0)); + + SqlBinary binary = reader.GetSqlBinary(0); + Assert.True(binary.IsNull); + + SqlBytes bytes = reader.GetSqlBytes(0); + Assert.True(bytes.IsNull); + Assert.Null(bytes.Buffer); + } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 8026aa479c..dba70d5fcd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -130,7 +130,7 @@ private static byte[] CreateBinaryTable(SqlConnection connection, string tableNa while (position < data.Length) { int copyCount = Math.Min(pattern.Length, data.Length - position); - Array.Copy(pattern, 0, data, position, copyCount); + Buffer.BlockCopy(pattern, 0, data, position, copyCount); position += copyCount; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs index d3bc3e54a9..f89017c811 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DateTimeTest/DateTimeTest.cs @@ -592,7 +592,7 @@ public static void TypeVersionKnobTest() } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] [InlineData(true)] [InlineData(false)] public static void BulkCopyTest(bool useReader) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index a52dee1ad4..c9dfe33bda 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.Threading.Tasks; using Xunit; @@ -251,6 +252,7 @@ public static void EnclavesConnectionExceptionTest() string connectionStringWithAttestationProtocol = DataTestUtility.TCPConnectionString + ";Attestation Protocol = HGS;"; string connectionStringWithAttestationURL = DataTestUtility.TCPConnectionString + ";Enclave Attestation URL = https://dummyURL;"; string connectionStringWithEnclave = connectionStringWithAttestationURL + ";Attestation Protocol = HGS;"; + string connectionStringWithNoneAttestationProtocol = DataTestUtility.TCPConnectionString + ";Attestation Protocol = None;"; InvalidOperationException e1 = Assert.Throws(() => new SqlConnection(connectionStringWithAttestationURL).Open()); Assert.Contains("You have specified the enclave attestation URL in the connection string", e1.Message); @@ -260,6 +262,14 @@ public static void EnclavesConnectionExceptionTest() InvalidOperationException e3 = Assert.Throws(() => new SqlConnection(connectionStringWithEnclave).Open()); Assert.Contains("You have specified the enclave attestation URL and attestation protocol in the connection string", e3.Message); + + if (DataTestUtility.EnclaveEnabled) + { + // connection should work if attestation protocol is None but no attestation url is provided + SqlConnection sqlConnection = new(connectionStringWithNoneAttestationProtocol); + sqlConnection.Open(); + Assert.Equal(ConnectionState.Open, sqlConnection.State); + } } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs index 3202636c3c..6edc9f00a6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Net; using System.Net.Sockets; +using System.Text; using System.Threading.Tasks; using Xunit; @@ -10,40 +13,96 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class InstanceNameTest { - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] public static void ConnectToSQLWithInstanceNameTest() { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); bool proceed = true; string dataSourceStr = builder.DataSource.Replace("tcp:", ""); string[] serverNamePartsByBackSlash = dataSourceStr.Split('\\'); + string hostname = serverNamePartsByBackSlash[0]; if (!dataSourceStr.Contains(",") && serverNamePartsByBackSlash.Length == 2) { - string hostname = serverNamePartsByBackSlash[0]; proceed = !string.IsNullOrWhiteSpace(hostname) && IsBrowserAlive(hostname); } if (proceed) { - using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) + using SqlConnection connection = new(builder.ConnectionString); + connection.Open(); + connection.Close(); + + if (builder.Encrypt != SqlConnectionEncryptOption.Strict) { - connection.Open(); - connection.Close(); + // Exercise the IP address-specific code in SSRP + IPAddress[] addresses = Dns.GetHostAddresses(hostname); + builder.DataSource = builder.DataSource.Replace(hostname, addresses[0].ToString()); + builder.TrustServerCertificate = true; + using SqlConnection connection2 = new(builder.ConnectionString); + connection2.Open(); + connection2.Close(); } } } + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse), nameof(DataTestUtility.AreConnStringsSetup))] + [InlineData(true, SqlConnectionIPAddressPreference.IPv4First)] + [InlineData(true, SqlConnectionIPAddressPreference.IPv6First)] + [InlineData(true, SqlConnectionIPAddressPreference.UsePlatformDefault)] + [InlineData(false, SqlConnectionIPAddressPreference.IPv4First)] + [InlineData(false, SqlConnectionIPAddressPreference.IPv6First)] + [InlineData(false, SqlConnectionIPAddressPreference.UsePlatformDefault)] + public static void ConnectManagedWithInstanceNameTest(bool useMultiSubnetFailover, SqlConnectionIPAddressPreference ipPreference) + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); + builder.MultiSubnetFailover = useMultiSubnetFailover; + builder.IPAddressPreference = ipPreference; + + Assert.True(DataTestUtility.ParseDataSource(builder.DataSource, out string hostname, out _, out string instanceName)); + + if (IsBrowserAlive(hostname) && IsValidInstance(hostname, instanceName)) + { + builder.DataSource = hostname + "\\" + instanceName; + + using SqlConnection connection = new(builder.ConnectionString); + connection.Open(); + } + + builder.ConnectTimeout = 2; + instanceName = "invalidinstance3456"; + if (!IsValidInstance(hostname, instanceName)) + { + builder.DataSource = hostname + "\\" + instanceName; + + using SqlConnection connection = new(builder.ConnectionString); + SqlException ex = Assert.Throws(() => connection.Open()); + Assert.Contains("Error Locating Server/Instance Specified", ex.Message); + } + } + private static bool IsBrowserAlive(string browserHostname) + { + const byte ClntUcastEx = 0x03; + + byte[] responsePacket = QueryBrowser(browserHostname, new byte[] { ClntUcastEx }); + return responsePacket != null && responsePacket.Length > 0; + } + + private static bool IsValidInstance(string browserHostName, string instanceName) + { + byte[] request = CreateInstanceInfoRequest(instanceName); + byte[] response = QueryBrowser(browserHostName, request); + return response != null && response.Length > 0; + } + + private static byte[] QueryBrowser(string browserHostname, byte[] requestPacket) { const int DefaultBrowserPort = 1434; const int sendTimeout = 1000; const int receiveTimeout = 1000; - const byte ClntUcastEx = 0x03; - - byte[] requestPacket = new byte[] { ClntUcastEx }; byte[] responsePacket = null; - using (UdpClient client = new UdpClient(AddressFamily.InterNetwork)) + using (UdpClient client = new(AddressFamily.InterNetwork)) { try { @@ -56,7 +115,21 @@ private static bool IsBrowserAlive(string browserHostname) } catch { } } - return responsePacket != null && responsePacket.Length > 0; + + return responsePacket; + } + + private static byte[] CreateInstanceInfoRequest(string instanceName) + { + const byte ClntUcastInst = 0x04; + instanceName += char.MinValue; + int byteCount = Encoding.ASCII.GetByteCount(instanceName); + + byte[] requestPacket = new byte[byteCount + 1]; + requestPacket[0] = ClntUcastInst; + Encoding.ASCII.GetBytes(instanceName, 0, instanceName.Length, requestPacket, 1); + + return requestPacket; } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs index b766502833..6cd19714ae 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/IntegratedAuthenticationTest/IntegratedAuthenticationTest.cs @@ -30,6 +30,26 @@ public static void IntegratedAuthenticationTestWithOutConnectionPooling() TryOpenConnectionWithIntegratedAuthentication(builder.ConnectionString); } + [ActiveIssue(21707)] + [ConditionalFact(nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))] + public static void IntegratedAuthenticationTest_InvalidServerSPN() + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); + builder.IntegratedSecurity = true; + builder.ServerSPN = "InvalidServerSPN"; + SqlException ex = Assert.Throws(() => TryOpenConnectionWithIntegratedAuthentication(builder.ConnectionString)); + Assert.Contains("generate SSPI context.", ex.Message); + } + + [ConditionalFact(nameof(IsIntegratedSecurityEnvironmentSet), nameof(AreConnectionStringsSetup))] + public static void IntegratedAuthenticationTest_ServerSPN() + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); + builder.IntegratedSecurity = true; + builder.ServerSPN = $"MSSQLSvc/{DataTestUtility.GetMachineFQDN()}"; + TryOpenConnectionWithIntegratedAuthentication(builder.ConnectionString); + } + private static void TryOpenConnectionWithIntegratedAuthentication(string connectionString) { using (SqlConnection connection = new SqlConnection(connectionString)) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs index ecbdb98a48..0c06e6cf47 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -1,20 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System; +using System.Diagnostics; +using System.Threading; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public static class LocalDBTest { + private enum InfoType + { + pipeName, + state + } private static bool IsLocalDBEnvironmentSet() => DataTestUtility.IsLocalDBInstalled(); private static bool IsLocalDbSharedInstanceSet() => DataTestUtility.IsLocalDbSharedInstanceSetup(); private static readonly string s_localDbConnectionString = @$"server=(localdb)\{DataTestUtility.LocalDbAppName}"; private static readonly string[] s_sharedLocalDbInstances = new string[] { @$"server=(localdb)\.\{DataTestUtility.LocalDbSharedInstanceName}", @$"server=(localdb)\." }; private static readonly string s_badConnectionString = $@"server=(localdb)\{DataTestUtility.LocalDbAppName};Database=DOES_NOT_EXIST;Pooling=false;"; + private static readonly string s_commandPrompt = "cmd.exe"; + private static readonly string s_sqlLocalDbInfo = @$"/c SqlLocalDb info {DataTestUtility.LocalDbAppName}"; + private static readonly string s_startLocalDbCommand = @$"/c SqlLocalDb start {DataTestUtility.LocalDbAppName}"; + private static readonly string s_localDbNamedPipeConnectionString = @$"server={GetLocalDbNamedPipe()}"; - static string LocalDbName = DataTestUtility.LocalDbAppName; #region LocalDbTests [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] @@ -36,6 +46,7 @@ public static void LocalDBEncryptionNotSupportedTest() [ConditionalFact(nameof(IsLocalDBEnvironmentSet))] public static void LocalDBMarsTest() { + RestartLocalDB(); ConnectionWithMarsTest(s_localDbConnectionString); } @@ -82,6 +93,36 @@ public static void SqlLocalDbSharedInstanceConnectionTest() } #endregion + #region NamedPipeTests + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ActiveIssue(20245)] //pending pipeline configuration + public static void SqlLocalDbNamedPipeConnectionTest() + { + ConnectionTest(s_localDbNamedPipeConnectionString); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ActiveIssue(20245)] //pending pipeline configuration + public static void LocalDBNamedPipeEncryptionNotSupportedTest() + { + // Encryption is not supported by SQL Local DB. + // But connection should succeed as encryption is disabled by driver. + ConnectionWithEncryptionTest(s_localDbNamedPipeConnectionString); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ActiveIssue(20245)] //pending pipeline configuration + public static void LocalDBNamepipeMarsTest() + { + ConnectionWithMarsTest(s_localDbNamedPipeConnectionString); + } + + #endregion + private static void ConnectionWithMarsTest(string connectionString) { SqlConnectionStringBuilder builder = new(connectionString) @@ -99,7 +140,7 @@ private static void ConnectionWithEncryptionTest(string connectionString) { IntegratedSecurity = true, ConnectTimeout = 2, - Encrypt = true + Encrypt = SqlConnectionEncryptOption.Mandatory }; OpenConnection(builder.ConnectionString); } @@ -123,5 +164,64 @@ private static void OpenConnection(string connString) var result = command.ExecuteScalar(); Assert.NotNull(result); } + + private static string GetLocalDbNamedPipe() + { + RestartLocalDB(); + string instanceName = ExecuteLocalDBCommandProcess(s_commandPrompt, s_sqlLocalDbInfo, InfoType.pipeName); + Assert.NotNull(instanceName); + Assert.NotEmpty(instanceName); + return instanceName; + } + + private static void RestartLocalDB() + { + string state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_sqlLocalDbInfo, InfoType.state); + int count = 5; + while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count>0) + { + count--; + state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_startLocalDbCommand, InfoType.state); + Thread.Sleep(2000); + } + if(state == null || state != "Running") + { + throw new LocalDBNotStartedException(); + } + } + private static string ExecuteLocalDBCommandProcess(string filename, string arguments, InfoType infoType) + { + ProcessStartInfo sInfo = new() + { + FileName = filename, + Arguments = arguments, + UseShellExecute = false, + CreateNoWindow = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; + string[] lines = Process.Start(sInfo).StandardOutput.ReadToEnd().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + + if (arguments == s_startLocalDbCommand) + { + Assert.Equal(2, lines.Length); + sInfo.Arguments = s_sqlLocalDbInfo; //after start check info again + lines = Process.Start(sInfo).StandardOutput.ReadToEnd().Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + } + Assert.Equal(9, lines.Length); + if (infoType.Equals(InfoType.state)) + { + return lines[5].Split(':')[1].Trim(); + } + else if (infoType.Equals(InfoType.pipeName)) + { + return lines[7].Split(new string[] { "Instance pipe name:" }, StringSplitOptions.None)[1].Trim(); + } + return null; + } + } + class LocalDBNotStartedException : Exception + { + public override string Message => "Unable to start LocalDB"; } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index b3c874dbcd..f8207f8a6a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -250,7 +250,7 @@ public static void MARSSyncBusyReaderTest() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async void MARSAsyncExecuteNonQueryTest() { using SqlConnection con = new(_connStr); @@ -297,7 +297,7 @@ public static void MARSSyncExecuteNonQueryTest() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async void MARSAsyncExecuteReaderTest1() { using SqlConnection con = new(_connStr); @@ -411,7 +411,7 @@ public static void MARSSyncExecuteReaderTest1() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async void MARSAsyncExecuteReaderTest2() { using SqlConnection con = new(_connStr); @@ -462,7 +462,7 @@ public static void MARSSyncExecuteReaderTest2() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async void MARSAsyncExecuteReaderTest3() { using SqlConnection con = new(_connStr); @@ -524,7 +524,7 @@ public static void MARSSyncExecuteReaderTest3() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async void MARSAsyncExecuteReaderTest4() { using SqlConnection con = new(_connStr); @@ -616,7 +616,7 @@ public static void MARSMultiDataReaderErrTest() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task MarsScenarioClientJoin() { SqlConnectionStringBuilder builder = new(_connStr); @@ -640,7 +640,7 @@ public static async Task MarsScenarioClientJoin() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void MarsConcurrencyTest() { var table = DataTestUtility.GenerateObjectName(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index fe0f1eb4a9..49cb91792e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -36,6 +36,8 @@ public static void CodeCoverageSqlClient() DataTestUtility.AssertThrowsWrapper(() => failValue = opc[0].ParameterName, "Invalid index 0 for this SqlParameterCollection with Count=0."); DataTestUtility.AssertThrowsWrapper(() => failValue = opc["@p1"].ParameterName, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); + + DataTestUtility.AssertThrowsWrapper(() => opc["@p1"] = null, "A SqlParameter with ParameterName '@p1' is not contained by this SqlParameterCollection."); } DataTestUtility.AssertThrowsWrapper(() => opc.Add(null), "The SqlParameterCollection only accepts non-null SqlParameter type objects."); @@ -95,11 +97,11 @@ public static void CodeCoverageSqlClient() new SqlCommand().Parameters.CopyTo(new object[0], 0); Assert.False(new SqlCommand().Parameters.GetEnumerator().MoveNext(), "FAILED: Expected MoveNext to be false"); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null SqlParameter type objects, not Int32 objects."); + DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Add(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null SqlParameter type objects, not Int32 objects."); + DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Insert(0, 0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); - DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null SqlParameter type objects, not Int32 objects."); + DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(0), "The SqlParameterCollection only accepts non-null Microsoft.Data.SqlClient.SqlParameter type objects, not System.Int32 objects."); DataTestUtility.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); } @@ -706,7 +708,7 @@ private static void EnableOptimizedParameterBinding_OutputFails() Assert.Contains("OptimizedParameterBinding", exception.Message); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] private static void EnableOptimizedParameterBinding_ReturnSucceeds() { int firstInput = 12; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs index 91676446d8..53e58c1542 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/StreamInputParam.cs @@ -93,7 +93,7 @@ private int ReadInternal(byte[] buffer, int offset, int count) } if (_errorPos >= _pos && _errorPos < _pos + nRead) throw new CustomStreamException(); - Array.Copy(_data, _pos, buffer, offset, nRead); + Buffer.BlockCopy(_data, _pos, buffer, offset, nRead); _pos += nRead; return nRead; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandStoredProcTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandStoredProcTest.cs new file mode 100644 index 0000000000..211e9c0d38 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandStoredProcTest.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ + public class SqlCommandStoredProcTest + { + private static readonly string s_tcp_connStr = DataTestUtility.TCPConnectionString; + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + public static void ShouldFailWithExceededLengthForSP() + { + string baseCommandText = "random text\u0000\u400a\u7300\u7400\u6100\u7400\u6500\u6d00\u6500\u6e00\u7400\u0000\u0006\u01ff\u0900\uf004\u0000\uffdc\u0001"; + string exceededLengthText = baseCommandText + new string(' ', 2000); + using SqlConnection conn = new(s_tcp_connStr); + conn.Open(); + using SqlCommand command = new() + { + Connection = conn, + CommandType = CommandType.StoredProcedure, + CommandText = exceededLengthText + }; + + // It should fail on the driver as the length of RPC is over 1046 + // 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523 + // each char takes 2 bytes. 523 * 2 = 1046 + Assert.Throws(() => command.ExecuteScalar()); + + command.CommandText = baseCommandText; + var ex = Assert.Throws(() => command.ExecuteScalar()); + Assert.StartsWith("Could not find stored procedure", ex.Message); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDSEnumeratorTest/SqlDataSourceEnumeratorTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDSEnumeratorTest/SqlDataSourceEnumeratorTest.cs new file mode 100644 index 0000000000..f5a9a063ac --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDSEnumeratorTest/SqlDataSourceEnumeratorTest.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.ServiceProcess; +using Microsoft.Data.Sql; +using Xunit; + +namespace Microsoft.Data.SqlClient.ManualTesting.Tests +{ +#if NET50_OR_LATER + [System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif + public class SqlDataSourceEnumeratorTest + { + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsNotUsingManagedSNIOnWindows))] + [PlatformSpecific(TestPlatforms.Windows)] + public void SqlDataSourceEnumerator_NativeSNI() + { + // The returned rows depends on the running services which could be zero or more. + int count = GetDSEnumerator().GetDataSources().Rows.Count; + Assert.InRange(count, 0, 65536); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUsingManagedSNI))] + [PlatformSpecific(TestPlatforms.Windows)] + public void SqlDataSourceEnumerator_ManagedSNI() + { + // After adding the managed SNI support, this test should have the same result as SqlDataSourceEnumerator_NativeSNI + Assert.Throws(() => GetDSEnumerator().GetDataSources()); + } + + private SqlDataSourceEnumerator GetDSEnumerator() + { + // SQL Server Browser runs as a Windows service. + // TODO: This assessment can be done on CI. + ServiceController[] services = ServiceController.GetServices(Environment.MachineName); + ServiceController service = services.FirstOrDefault(s => s.ServiceName == "SQLBrowser"); + if (service != null) + { + Assert.Equal(ServiceControllerStatus.Running, service.Status); + } + return SqlDataSourceEnumerator.Instance; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs index 1bf5bde2a1..09c369e11e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlFileStreamTest/SqlFileStreamTest.cs @@ -27,14 +27,15 @@ public static void ReadFilestream() { string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { - InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString) + InitialCatalog = SetupFileStreamDB(), + IntegratedSecurity = true }.ConnectionString; - using SqlConnection connection = new(connString); - connection.Open(); - string tempTable = SetupTable(connection); + string tempTable = SetupTable(connString); int nRow = 0; byte[] retrievedValue; + using SqlConnection connection = new(connString); + connection.Open(); SqlCommand command = new($"SELECT Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection); try { @@ -72,12 +73,12 @@ public static void ReadFilestream() finally { // Drop Table - ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection); + ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connString); } } finally { - DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString); + DropFileStreamDb(DataTestUtility.TCPConnectionString); } } @@ -89,12 +90,11 @@ public static void OverwriteFilestream() { string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { - InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString) + InitialCatalog = SetupFileStreamDB(), + IntegratedSecurity = true }.ConnectionString; - using SqlConnection connection = new(connString); - connection.Open(); - string tempTable = SetupTable(connection); + string tempTable = SetupTable(connString); byte[] insertedValue = BitConverter.GetBytes(3); // Reverse the byte array, if the system architecture is little-endian. @@ -102,6 +102,8 @@ public static void OverwriteFilestream() Array.Reverse(insertedValue); try { + using SqlConnection connection = new(connString); + connection.Open(); SqlCommand command = new($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection); SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); @@ -124,18 +126,18 @@ public static void OverwriteFilestream() transaction.Commit(); // Compare inserted and retrieved value - byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length); + byte[] retrievedValue = RetrieveData(tempTable, connString, insertedValue.Length); Assert.Equal(insertedValue, retrievedValue); } finally { // Drop Table - ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection); + ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connString); } } finally { - DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString); + DropFileStreamDb(DataTestUtility.TCPConnectionString); } } @@ -147,12 +149,11 @@ public static void AppendFilestream() { string connString = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { - InitialCatalog = SetupFileStreamDB(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString) + InitialCatalog = SetupFileStreamDB(), + IntegratedSecurity = true }.ConnectionString; - using SqlConnection connection = new(connString); - connection.Open(); - string tempTable = SetupTable(connection); + string tempTable = SetupTable(connString); byte[] insertedValue = BitConverter.GetBytes(s_insertedValues[0]); byte appendedByte = 0x04; @@ -164,6 +165,8 @@ public static void AppendFilestream() try { + using SqlConnection connection = new(connString); + connection.Open(); SqlCommand command = new($"SELECT TOP(1) Photo.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(),EmployeeId FROM {tempTable} ORDER BY EmployeeId", connection); SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); @@ -188,26 +191,27 @@ public static void AppendFilestream() transaction.Commit(); // Compare inserted and retrieved value - byte[] retrievedValue = RetrieveData(tempTable, connection, insertedValue.Length); + byte[] retrievedValue = RetrieveData(tempTable, connString, insertedValue.Length); Assert.Equal(insertedValue, retrievedValue); } finally { // Drop Table - ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connection); + ExecuteNonQueryCommand($"DROP TABLE {tempTable}", connString); } } finally { - DropFileStreamDb(ref DataTestUtility.FileStreamDirectory, DataTestUtility.TCPConnectionString); + DropFileStreamDb(DataTestUtility.TCPConnectionString); } } #region Private helper methods - private static string SetupFileStreamDB(ref string fileStreamDir, string connString) + private static string SetupFileStreamDB() { + string fileStreamDir = DataTestUtility.FileStreamDirectory; try { if (fileStreamDir != null) @@ -228,7 +232,7 @@ FILEGROUP FileStreamGroup CONTAINS FILESTREAM LOG ON (NAME = PhotoLibrary_log, FILENAME = '{fileStreamDir}PhotoLibrary_log.ldf')"; - using SqlConnection con = new(new SqlConnectionStringBuilder(connString) { InitialCatalog = "master" }.ConnectionString); + using SqlConnection con = new(new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { InitialCatalog = "master", IntegratedSecurity = true }.ConnectionString); con.Open(); using SqlCommand cmd = con.CreateCommand(); cmd.CommandText = createDBQuery; @@ -244,7 +248,7 @@ LOG ON return s_fileStreamDBName; } - private static void DropFileStreamDb(ref string fileStreamDir, string connString) + private static void DropFileStreamDb(string connString) { try { @@ -256,44 +260,50 @@ private static void DropFileStreamDb(ref string fileStreamDir, string connString catch (SqlException e) { Console.WriteLine("File Stream database could not be dropped. " + e.Message); - fileStreamDir = null; } } - private static string SetupTable(SqlConnection conn) + private static string SetupTable(string connString) { // Generate random table name - string tempTable = "fs_" + Guid.NewGuid().ToString().Replace('-', '_'); - + string tempTable = DataTestUtility.GetUniqueNameForSqlServer("fs"); // Create table string createTable = $"CREATE TABLE {tempTable} (EmployeeId INT NOT NULL PRIMARY KEY, Photo VARBINARY(MAX) FILESTREAM NULL, RowGuid UNIQUEIDENTIFIER NOT NULL ROWGUIDCOL UNIQUE DEFAULT NEWID() ) "; - ExecuteNonQueryCommand(createTable, conn); + ExecuteNonQueryCommand(createTable, connString); // Insert data into created table for (int i = 0; i < s_insertedValues.Length; i++) { string prepTable = $"INSERT INTO {tempTable} VALUES ({i + 1}, {s_insertedValues[i]} , default)"; - ExecuteNonQueryCommand(prepTable, conn); + ExecuteNonQueryCommand(prepTable, connString); } return tempTable; } - private static void ExecuteNonQueryCommand(string cmdText, SqlConnection conn) + private static void ExecuteNonQueryCommand(string cmdText, string connString) { - using SqlCommand cmd = conn.CreateCommand(); - cmd.CommandText = cmdText; - cmd.ExecuteNonQuery(); + using (SqlConnection conn = new SqlConnection(connString)) + { + conn.Open(); + using SqlCommand cmd = conn.CreateCommand(); + cmd.CommandText = cmdText; + cmd.ExecuteNonQuery(); + } } - private static byte[] RetrieveData(string tempTable, SqlConnection conn, int len) + private static byte[] RetrieveData(string tempTable, string connString, int len) { - SqlCommand command = new($"SELECT TOP(1) Photo FROM {tempTable}", conn); byte[] bArray = new byte[len]; - using (SqlDataReader reader = command.ExecuteReader()) + using (SqlConnection conn = new SqlConnection(connString)) { - reader.Read(); - reader.GetBytes(0, 0, bArray, 0, len); + conn.Open(); + SqlCommand command = new($"SELECT TOP(1) Photo FROM {tempTable}", conn); + using (SqlDataReader reader = command.ExecuteReader()) + { + reader.Read(); + reader.GetBytes(0, 0, bArray, 0, len); + } } return bArray; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index 3015d11c70..0e64b091b6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -2,10 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +#if NETFRAMEWORK +using System; +using System.Globalization; +using Microsoft.SqlServer.Types; +#else using System.Collections.ObjectModel; -using System.Data; using System.Data.Common; +#endif +using System.Collections.Generic; +using System.Data; using System.Data.SqlTypes; using System.IO; using System.Linq; @@ -404,5 +410,114 @@ private static string ToHexString(byte[] bytes) return hex.ToString(); } + +#if NETFRAMEWORK + private static string GetUdtName(Type udtClrType) + { + if (typeof(SqlHierarchyId) == udtClrType) + { + return "hierarchyid"; + } + if (typeof(SqlGeography) == udtClrType) + { + return "geography"; + } + if (typeof(SqlGeometry) == udtClrType) + { + return "geometry"; + } + + throw new ArgumentException("Unknwon UDT CLR Type " + udtClrType.FullName); + } + + // Synapse: Parse error at line: 1, column: 8: Incorrect syntax near 'geometry'. + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + public static void TestSqlServerTypesInsertAndRead() + { + string tableName = DataTestUtility.GetUniqueNameForSqlServer("Type"); + string allTypesSQL = @$" + if not exists (select * from sysobjects where name='{tableName}' and xtype='U') + Begin + create table {tableName} + ( + id int identity not null, + c1 hierarchyid not null, + c2 uniqueidentifier not null, + c3 geography not null, + c4 geometry not null, + ); + End + "; + + Dictionary rowValues = new(); + rowValues["c1"] = SqlHierarchyId.Parse(new SqlString("/1/1/3/")); + rowValues["c2"] = Guid.NewGuid(); + rowValues["c3"] = SqlGeography.Point(1.1, 2.2, 4120); + rowValues["c4"] = SqlGeometry.Point(5.2, 1.1, 4120); + + using SqlConnection conn = new(DataTestUtility.TCPConnectionString); + conn.Open(); + try + { + using SqlCommand cmd1 = conn.CreateCommand(); + + // Create db and table + cmd1.CommandText = allTypesSQL.ToString(); + cmd1.ExecuteNonQuery(); + + using SqlCommand cmd2 = conn.CreateCommand(); + + StringBuilder columnsSql = new(); + StringBuilder valuesSql = new(); + + foreach (KeyValuePair pair in rowValues) + { + string paramName = "@" + pair.Key; + object paramValue = pair.Value; + columnsSql.Append(pair.Key); + valuesSql.Append(paramName); + + columnsSql.Append(","); + valuesSql.Append(","); + + SqlParameter param = new(paramName, paramValue); + cmd2.Parameters.Add(param); + + if (paramValue.GetType().Assembly == typeof(SqlHierarchyId).Assembly) + { + param.UdtTypeName = GetUdtName(paramValue.GetType()); + } + } + + columnsSql.Length--; + valuesSql.Length--; + + string insertSql = string.Format(CultureInfo.InvariantCulture, $"insert {tableName}" + @" ({0}) values({1})", + columnsSql.ToString(), valuesSql.ToString()); + + cmd2.CommandText = insertSql; + cmd2.ExecuteNonQuery(); + + cmd1.CommandText = @$"select * from dbo.{tableName}"; + using SqlDataReader r = cmd1.ExecuteReader(); + while (r.Read()) + { + Assert.Equal(rowValues["c1"].GetType(), r.GetValue(1).GetType()); + Assert.Equal(rowValues["c2"].GetType(), r.GetValue(2).GetType()); + Assert.Equal(rowValues["c3"].GetType(), r.GetValue(3).GetType()); + Assert.Equal(rowValues["c4"].GetType(), r.GetValue(4).GetType()); + + Assert.Equal(rowValues["c1"].ToString(), r.GetValue(1).ToString()); + Assert.Equal(rowValues["c2"].ToString(), r.GetValue(2).ToString()); + Assert.Equal(rowValues["c3"].ToString(), r.GetValue(3).ToString()); + Assert.Equal(rowValues["c4"].ToString(), r.GetValue(4).ToString()); + } + } + finally + { + DataTestUtility.DropTable(conn, tableName); + } + } +#endif } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs index 8d66060684..ed1e683002 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs @@ -5,7 +5,7 @@ using System; using System.Data.SqlTypes; using System.IO; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 500)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj index 5fcf9a6d08..350733ed9b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.csproj @@ -12,6 +12,7 @@ + - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs index 5d82e6896d..e44f8d3abe 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.cs @@ -6,7 +6,7 @@ using System.Data.SqlTypes; using System.IO; using System.Text; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 30)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj index 5b0d1542d3..5f61786bb3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Circle.csproj @@ -12,6 +12,7 @@ + - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs index db88aa35e4..7ccc5c6d4b 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Circle/Point1.cs @@ -6,7 +6,7 @@ using System.Data.SqlTypes; using System.IO; using System.Text; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 9)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs index 777455fdfd..d6efa7ddf5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Line.cs @@ -6,7 +6,7 @@ using System.Data.SqlTypes; using System.IO; using System.Text; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = false, MaxByteSize = 20)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs index 1f122fecd7..2dbf5e510c 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Point.cs @@ -6,7 +6,7 @@ using System.Data.SqlTypes; using System.IO; using System.Text; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; [Serializable] [SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, MaxByteSize = 9)] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj index b2394ad310..e0ea118c00 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Shapes/Shapes.csproj @@ -12,6 +12,7 @@ + - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs index e49876dda5..e026674602 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.cs @@ -5,7 +5,7 @@ using System; using System.Data.SqlTypes; using System.Globalization; -using Microsoft.Data.SqlClient.Server; +using Microsoft.SqlServer.Server; namespace Microsoft.Samples.SqlServer { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj index bf3da8f47a..5e8ce85bd4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Utf8String/Utf8String.csproj @@ -12,6 +12,7 @@ + - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs index 3437736adf..1f07242ee3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -6,8 +6,8 @@ using System.Data; using System.Data.SqlTypes; using System.Text; -using Microsoft.Data.SqlClient.Server; using Xunit; +using Microsoft.SqlServer.Server; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { @@ -570,7 +570,7 @@ public void TestSchemaTable() public void TestSqlUserDefinedAggregateAttributeMaxByteSize() { Func create - = (size) => new SqlUserDefinedAggregateAttribute(Format.UserDefined) { MaxByteSize = size }; + = (size) => new(Format.UserDefined) { MaxByteSize = size }; SqlUserDefinedAggregateAttribute attribute1 = create(-1); SqlUserDefinedAggregateAttribute attribute2 = create(0); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs index c6bc56d9f4..6aae0c555a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Utf8SupportTest/Utf8SupportTest.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Text; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -29,6 +31,65 @@ public static void CheckSupportUtf8ConnectionProperty() } } - // TODO: Write tests using UTF8 collations + + // skip creating database on Azure + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer), nameof(DataTestUtility.IsNotAzureSynapse))] + public static void UTF8databaseTest() + { + const string letters = @"!\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f€\u0081‚ƒ„…†‡ˆ‰Š‹Œ\u008dŽ\u008f\u0090‘’“”•–—˜™š›œ\u009džŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; + string dbName = DataTestUtility.GetUniqueNameForSqlServer("UTF8databaseTest", false); + string tblName = "Table1"; + + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString); + builder.InitialCatalog = "master"; + + using SqlConnection cn = new(builder.ConnectionString); + cn.Open(); + + try + { + PrepareDatabaseUTF8(cn, dbName, tblName, letters); + + builder.InitialCatalog = dbName; + using SqlConnection cnnTest = new(builder.ConnectionString); + // creating a databse is a time consumer action and could be retried. + SqlRetryLogicOption retryOption = new() { NumberOfTries = 3, DeltaTime = TimeSpan.FromMilliseconds(200) }; + cnnTest.RetryLogicProvider = SqlConfigurableRetryFactory.CreateIncrementalRetryProvider(retryOption); + cnnTest.Open(); + + using SqlCommand cmd = cnnTest.CreateCommand(); + cmd.CommandText = $"SELECT * FROM {tblName}"; + + using SqlDataReader reader = cmd.ExecuteReader(); + + Assert.True(reader.Read(), "The test table should have a row!"); + object[] data = new object[1]; + reader.GetSqlValues(data); + Assert.Equal(letters, data[0].ToString()); + reader.Close(); + cnnTest.Close(); + } + finally + { + DataTestUtility.DropDatabase(cn, dbName); + } + } + + private static void PrepareDatabaseUTF8(SqlConnection cnn, string dbName, string tblName, string letters) + { + StringBuilder sb = new(); + + using SqlCommand cmd = cnn.CreateCommand(); + + cmd.CommandText = $"CREATE DATABASE [{dbName}] COLLATE Latin1_General_100_CI_AS_SC_UTF8;"; + cmd.ExecuteNonQuery(); + + sb.AppendLine($"CREATE TABLE [{dbName}].dbo.[{tblName}] (col VARCHAR(7633) COLLATE Latin1_General_100_CI_AS_SC);"); + sb.AppendLine($"INSERT INTO [{dbName}].dbo.[{tblName}] VALUES (@letters);"); + + cmd.Parameters.Add(new SqlParameter("letters", letters)); + cmd.CommandText = sb.ToString(); + cmd.ExecuteNonQuery(); + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs index 0a5e44bd9f..511ff315df 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventCounterTest.cs @@ -149,7 +149,8 @@ public void EventCounter_StasisCounters_Functional() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void EventCounter_ReclaimedConnectionsCounter_Functional() { - SqlConnection.ClearAllPools(); + // clean pools and pool groups + ClearConnectionPools(); var stringBuilder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { Pooling = true, MaxPoolSize = 1 }; long rc = SqlClientEventSourceProps.ReclaimedConnections; @@ -158,6 +159,7 @@ public void EventCounter_ReclaimedConnectionsCounter_Functional() // Specifying the generation number makes it to run faster by avoiding a full GC process GC.Collect(gcNumber); GC.WaitForPendingFinalizers(); + System.Threading.Thread.Sleep(200); // give the pooler some time to reclaim the connection and avoid the conflict. using (SqlConnection conn = new SqlConnection(stringBuilder.ToString())) { @@ -220,26 +222,32 @@ private static InternalConnectionWrapper CreateEmancipatedConnection(string conn private void ClearConnectionPools() { //ClearAllPoos kills all the existing pooled connection thus deactivating all the active pools - var liveConnectionPools = SqlClientEventSourceProps.ActiveConnectionPools + + long liveConnectionPools = SqlClientEventSourceProps.ActiveConnectionPools + SqlClientEventSourceProps.InactiveConnectionPools; SqlConnection.ClearAllPools(); Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, liveConnectionPools); Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPools); - //the 1st PruneConnectionPoolGroups call cleans the dangling inactive connection pools + long icp = SqlClientEventSourceProps.InactiveConnectionPools; + + // The 1st PruneConnectionPoolGroups call cleans the dangling inactive connection pools. PruneConnectionPoolGroups(); - Assert.Equal(0, SqlClientEventSourceProps.InactiveConnectionPools); + // If the pool isn't empty, it's because there are active connections or distributed transactions that need it. + Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPools, 0, icp); //the 2nd call deactivates the dangling connection pool groups - var liveConnectionPoolGroups = SqlClientEventSourceProps.ActiveConnectionPoolGroups + + long liveConnectionPoolGroups = SqlClientEventSourceProps.ActiveConnectionPoolGroups + SqlClientEventSourceProps.InactiveConnectionPoolGroups; + long acpg = SqlClientEventSourceProps.ActiveConnectionPoolGroups; PruneConnectionPoolGroups(); Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, liveConnectionPoolGroups); - Assert.Equal(0, SqlClientEventSourceProps.ActiveConnectionPoolGroups); + // If the pool entry isn't empty, it's because there are active pools that need it. + Assert.InRange(SqlClientEventSourceProps.ActiveConnectionPoolGroups, 0, acpg); + long icpg = SqlClientEventSourceProps.InactiveConnectionPoolGroups; //the 3rd call cleans the dangling connection pool groups PruneConnectionPoolGroups(); - Assert.Equal(0, SqlClientEventSourceProps.InactiveConnectionPoolGroups); + Assert.InRange(SqlClientEventSourceProps.InactiveConnectionPoolGroups, 0, icpg); } private static void PruneConnectionPoolGroups() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs index 13f6f7926a..3552204886 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/TestTdsServer.cs @@ -43,7 +43,7 @@ public static TestTdsServer StartServerWithQueryEngine(QueryEngine engine, bool server._endpoint.Start(); int port = server._endpoint.ServerEndPoint.Port; - server.connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = 5, Encrypt = false }; + server.connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = "localhost," + port, ConnectTimeout = 5, Encrypt = SqlConnectionEncryptOption.Optional }; server.ConnectionString = server.connectionStringBuilder.ConnectionString; return server; } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs index a09c9313a8..6a936ac5a1 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.ExtUtilities/SqlDbManager.cs @@ -55,6 +55,7 @@ public static void Run(string[] args) builder.InitialCatalog = DB_Master; using (SqlConnection conn = new SqlConnection(builder.ConnectionString)) { + Console.WriteLine($"Connecting to {builder.DataSource}"); SqlServer.Management.Smo.Server server = new SqlServer.Management.Smo.Server(new ServerConnection(conn)); ServerConnection context = server.ConnectionContext; @@ -102,7 +103,7 @@ public static void Run(string[] args) } catch (Exception e) { - throw new Exception($"{args[0]} execution failed with Error: {e.Message}"); + throw new Exception($"{args[0]} execution failed with Error: {e}"); } } @@ -158,6 +159,7 @@ private static void UpdateConfig(string key, SqlConnectionStringBuilder builder) private static void DropIfExistsDatabase(string dbName, ServerConnection context) { + Console.WriteLine($"Dropping database [{dbName}] if it exists"); try { string dropScript = $"IF EXISTS (select * from sys.databases where name = '{dbName}') BEGIN DROP DATABASE [{dbName}] END;"; @@ -165,7 +167,7 @@ private static void DropIfExistsDatabase(string dbName, ServerConnection context } catch (ExecutionFailureException ex) { - Console.WriteLine($"FAILED to drop database '{dbName}'. Error message: {ex.Message}"); + Console.WriteLine($"FAILED to drop database '{dbName}'. Error message: {ex}"); } } @@ -174,6 +176,7 @@ private static void CreateDatabase(string dbName, ServerConnection context) DropIfExistsDatabase(dbName, context); string createScript = File.ReadAllText(NorthWindScriptPath); + Console.WriteLine($"Creating database [{dbName}]"); try { createScript = createScript.Replace(DB_Northwind, dbName); @@ -183,7 +186,7 @@ private static void CreateDatabase(string dbName, ServerConnection context) } catch (ExecutionFailureException ex) { - Console.WriteLine(ex.Message); + Console.WriteLine(ex); throw; } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs index dec84eabd1..101b0c0606 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/Config.cs @@ -14,6 +14,7 @@ public class Config public string NPConnectionString = null; public string TCPConnectionStringHGSVBS = null; public string TCPConnectionStringAASVBS = null; + public string TCPConnectionStringNoneVBS = null; public string TCPConnectionStringAASSGX = null; public string AADAuthorityURL = null; public string AADPasswordConnectionString = null; diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json index 043b73c4c9..cbe4c15e70 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/config.default.json @@ -3,6 +3,7 @@ "NPConnectionString": "Data Source=np:localhost;Database=Northwind;Integrated Security=true;", "TCPConnectionStringHGSVBS": "", "TCPConnectionStringAASVBS": "", + "TCPConnectionStringNoneVBS": "", "TCPConnectionStringAASSGX": "", "EnclaveEnabled": false, "TracingEnabled": false, @@ -16,6 +17,7 @@ "AzureKeyVaultClientSecret": "", "SupportsIntegratedSecurity": true, "LocalDbAppName": "", + "LocalDbSharedInstanceName": "", "SupportsFileStream": false, "FileStreamDirectory": "", "UseManagedSNIOnWindows": false, diff --git a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj index 1f2e0aa3ee..9d3f813a99 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj +++ b/src/Microsoft.Data.SqlClient/tests/tools/Microsoft.DotNet.XUnitExtensions/Microsoft.DotNet.XUnitExtensions.csproj @@ -51,5 +51,6 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs index 421e3b275a..535304964d 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS.Servers/GenericTDSServer.cs @@ -739,8 +739,11 @@ protected virtual TDSMessageCollection CheckTDSVersion(ITDSServerSession session private byte[] _GenerateRandomBytes(int count) { byte[] randomBytes = new byte[count]; - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } + return randomBytes; } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/Login7/TDSLogin7FedAuthOptionToken.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/Login7/TDSLogin7FedAuthOptionToken.cs index 412664ae1a..4583601fd8 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/Login7/TDSLogin7FedAuthOptionToken.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/Login7/TDSLogin7FedAuthOptionToken.cs @@ -366,8 +366,11 @@ private bool ReadSecurityTokenLogin(Stream source, uint optionDataLength) private byte[] _GenerateRandomBytes(int count) { byte[] randomBytes = new byte[count]; - RandomNumberGenerator rng = RandomNumberGenerator.Create(); - rng.GetBytes(randomBytes); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(randomBytes); + } + return randomBytes; } } diff --git a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSStream.cs b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSStream.cs index 90a295bbc1..94abbf5818 100644 --- a/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSStream.cs +++ b/src/Microsoft.Data.SqlClient/tests/tools/TDS/TDS/TDSStream.cs @@ -349,7 +349,7 @@ public override void Write(byte[] buffer, int offset, int count) if (packetDataToWrite > 0) { // Append new data to the end of the packet data - Array.Copy(buffer, bufferWrittenPosition + offset, _outgoingPacket, OutgoingPacketHeader.Length, packetDataToWrite); + Buffer.BlockCopy(buffer, bufferWrittenPosition + offset, _outgoingPacket, OutgoingPacketHeader.Length, packetDataToWrite); // Register that we've written new data bufferWrittenPosition += packetDataToWrite; diff --git a/src/Microsoft.SqlServer.Server/IBinarySerialize.cs b/src/Microsoft.SqlServer.Server/IBinarySerialize.cs new file mode 100644 index 0000000000..a975887161 --- /dev/null +++ b/src/Microsoft.SqlServer.Server/IBinarySerialize.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; + +namespace Microsoft.SqlServer.Server +{ + /// + // This interface is used by types that want full control over the + // binary serialization format. + public interface IBinarySerialize + { + /// + // Read from the specified binary reader. + void Read(BinaryReader r); + /// + // Write to the specified binary writer. + void Write(BinaryWriter w); + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/InvalidUdtException.cs b/src/Microsoft.SqlServer.Server/InvalidUdtException.cs similarity index 71% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/InvalidUdtException.cs rename to src/Microsoft.SqlServer.Server/InvalidUdtException.cs index 8a82a0868f..395b02af61 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/InvalidUdtException.cs +++ b/src/Microsoft.SqlServer.Server/InvalidUdtException.cs @@ -4,11 +4,10 @@ using System; using System.Runtime.Serialization; -using Microsoft.Data.Common; -namespace Microsoft.Data.SqlClient.Server +namespace Microsoft.SqlServer.Server { - /// + /// [Serializable] public sealed class InvalidUdtException : SystemException { @@ -33,19 +32,19 @@ private InvalidUdtException(SerializationInfo si, StreamingContext sc) : base(si { } - /// + /// [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags = System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)] public override void GetObjectData(SerializationInfo si, StreamingContext context) { base.GetObjectData(si, context); } - internal static InvalidUdtException Create(Type udtType, string resourceReason) + /// + public static InvalidUdtException Create(Type udtType, string resourceReason = "SqlUdtReason_NoUdtAttribute") { string reason = StringsHelper.GetString(resourceReason); string message = StringsHelper.GetString(Strings.SqlUdt_InvalidUdtMessage, udtType.FullName, reason); InvalidUdtException e = new InvalidUdtException(message); - ADP.TraceExceptionAsReturnValue(e); return e; } } diff --git a/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj new file mode 100644 index 0000000000..dafc9a4dbe --- /dev/null +++ b/src/Microsoft.SqlServer.Server/Microsoft.SqlServer.Server.csproj @@ -0,0 +1,48 @@ + + + Microsoft.SqlServer.Server + This is the helper library for Microsoft.Data.SqlClient for cross framework compatibility support of UDT types. + Microsoft SqlServer Server + net46;netstandard2.0 + $(ObjFolder)$(Configuration).$(Platform)\$(AssemblyName)\ + $(BinFolder)$(Configuration).$(Platform)\$(AssemblyName)\ + $(OutputPath)$(TargetFramework)\Microsoft.SqlServer.Server.xml + portable + false + true + + + + + + + + + + + + + + + + + + + + + + True + True + Strings.resx + + + ResXFileCodeGenerator + Strings.Designer.cs + + + + + + + + diff --git a/src/Microsoft.SqlServer.Server/SqlFacetAttribute.cs b/src/Microsoft.SqlServer.Server/SqlFacetAttribute.cs new file mode 100644 index 0000000000..ba5cd82683 --- /dev/null +++ b/src/Microsoft.SqlServer.Server/SqlFacetAttribute.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.SqlServer.Server +{ + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + public class SqlFacetAttribute : Attribute + { + /// + public SqlFacetAttribute() { } + + /// + public bool IsFixedLength + { + get; + set; + } + + /// + public int MaxSize + { + get; + set; + } + + /// + public int Precision + { + get; + set; + } + + /// + public int Scale + { + get; + set; + } + + /// + public bool IsNullable + { + get; + set; + } + } +} diff --git a/src/Microsoft.SqlServer.Server/SqlFunctionAttribute.cs b/src/Microsoft.SqlServer.Server/SqlFunctionAttribute.cs new file mode 100644 index 0000000000..4d735cd77f --- /dev/null +++ b/src/Microsoft.SqlServer.Server/SqlFunctionAttribute.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.SqlServer.Server +{ + /// + [Serializable] + public enum DataAccessKind + { + /// + None = 0, + /// + Read = 1, + } + + /// + [Serializable] + public enum SystemDataAccessKind + { + /// + None = 0, + /// + Read = 1, + } + + /// + // sql specific attribute + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false), Serializable] + public class SqlFunctionAttribute : Attribute + { + private bool _isDeterministic; + private DataAccessKind _dataAccess; + private SystemDataAccessKind _systemDataAccess; + private bool _isPrecise; + private string _name; + private string _tableDefinition; + private string _fillRowMethodName; + + /// + public SqlFunctionAttribute() + { + // default values + _isDeterministic = false; + _dataAccess = DataAccessKind.None; + _systemDataAccess = SystemDataAccessKind.None; + _isPrecise = false; + _name = null; + _tableDefinition = null; + _fillRowMethodName = null; + } + + /// + public bool IsDeterministic + { + get => _isDeterministic; + set => _isDeterministic = value; + } + + /// + public DataAccessKind DataAccess + { + get => _dataAccess; + set => _dataAccess = value; + } + + /// + public SystemDataAccessKind SystemDataAccess + { + get => _systemDataAccess; + set => _systemDataAccess = value; + } + + /// + public bool IsPrecise + { + get => _isPrecise; + set => _isPrecise = value; + } + + /// + public string Name + { + get => _name; + set => _name = value; + } + + /// + public string TableDefinition + { + get => _tableDefinition; + set => _tableDefinition = value; + } + + /// + public string FillRowMethodName + { + get => _fillRowMethodName; + set => _fillRowMethodName = value; + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMethodAttribute.cs b/src/Microsoft.SqlServer.Server/SqlMethodAttribute.cs similarity index 55% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMethodAttribute.cs rename to src/Microsoft.SqlServer.Server/SqlMethodAttribute.cs index 4b29c6df55..a28afa2251 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlMethodAttribute.cs +++ b/src/Microsoft.SqlServer.Server/SqlMethodAttribute.cs @@ -4,9 +4,9 @@ using System; -namespace Microsoft.Data.SqlClient.Server +namespace Microsoft.SqlServer.Server { - /// + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false), Serializable] public sealed class SqlMethodAttribute : SqlFunctionAttribute { @@ -14,7 +14,7 @@ public sealed class SqlMethodAttribute : SqlFunctionAttribute private bool _isMutator; private bool _shouldInvokeIfReceiverIsNull; - /// + /// public SqlMethodAttribute() { // default values @@ -23,21 +23,21 @@ public SqlMethodAttribute() _shouldInvokeIfReceiverIsNull = false; } - /// + /// public bool OnNullCall { get => _isCalledOnNullInputs; set => _isCalledOnNullInputs = value; } - /// + /// public bool IsMutator { get => _isMutator; set => _isMutator = value; } - /// + /// public bool InvokeIfReceiverIsNull { get => _shouldInvokeIfReceiverIsNull; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedAggregateAttribute.cs b/src/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.cs similarity index 54% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedAggregateAttribute.cs rename to src/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.cs index 861cc8cef0..6e3e949f16 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedAggregateAttribute.cs +++ b/src/Microsoft.SqlServer.Server/SqlUserDefinedAggregateAttribute.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.Data.Common; +using System.Globalization; -namespace Microsoft.Data.SqlClient.Server +namespace Microsoft.SqlServer.Server { - /// + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] public sealed class SqlUserDefinedAggregateAttribute : Attribute { @@ -19,11 +19,11 @@ public sealed class SqlUserDefinedAggregateAttribute : Attribute private Format _format; private string _name; - /// + /// // The maximum value for the maxbytesize field, in bytes. public const int MaxByteSizeValue = 8000; - /// + /// // A required attribute on all UD Aggs, used to indicate that the // given type is a UD Agg, and its storage format. public SqlUserDefinedAggregateAttribute(Format format) @@ -31,17 +31,17 @@ public SqlUserDefinedAggregateAttribute(Format format) switch (format) { case Format.Unknown: - throw ADP.NotSupportedUserDefinedTypeSerializationFormat(format, nameof(format)); + throw new ArgumentOutOfRangeException(typeof(Format).Name, string.Format(Strings.ADP_NotSupportedEnumerationValue, typeof(Format).Name, ((int)format).ToString(), nameof(format))); case Format.Native: case Format.UserDefined: _format = format; break; default: - throw ADP.InvalidUserDefinedTypeSerializationFormat(format); + throw new ArgumentOutOfRangeException(typeof(Format).Name, string.Format(Strings.ADP_InvalidEnumerationValue, typeof(Format).Name, ((int)format).ToString(CultureInfo.InvariantCulture))); } } - /// + /// // The maximum size of this instance, in bytes. Does not have to be // specified for Native format serialization. The maximum value // for this property is specified by MaxByteSizeValue. @@ -56,13 +56,13 @@ public int MaxByteSize // MaxByteSize of -1 means 2GB and is valid, as well as 0 to MaxByteSizeValue if (value < -1 || value > MaxByteSizeValue) { - throw ADP.ArgumentOutOfRange(StringsHelper.GetString(Strings.SQLUDT_MaxByteSizeValue), nameof(MaxByteSize), value); + throw new ArgumentOutOfRangeException(nameof(MaxByteSize), value.ToString(), StringsHelper.GetString(Strings.SQLUDT_MaxByteSizeValue)); } _maxByteSize = value; } } - /// + /// public bool IsInvariantToDuplicates { get @@ -75,7 +75,7 @@ public bool IsInvariantToDuplicates } } - /// + /// public bool IsInvariantToNulls { get @@ -88,7 +88,7 @@ public bool IsInvariantToNulls } } - /// + /// public bool IsInvariantToOrder { get @@ -101,7 +101,7 @@ public bool IsInvariantToOrder } } - /// + /// public bool IsNullIfEmpty { get @@ -114,11 +114,11 @@ public bool IsNullIfEmpty } } - /// + /// // The on-disk format for this type. public Format Format => _format; - /// + /// public string Name { get diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedTypeAttribute.cs b/src/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.cs similarity index 56% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedTypeAttribute.cs rename to src/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.cs index 1a399d2749..0f24b89150 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlUserDefinedTypeAttribute.cs +++ b/src/Microsoft.SqlServer.Server/SqlUserDefinedTypeAttribute.cs @@ -3,22 +3,22 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.Data.Common; +using System.Globalization; -namespace Microsoft.Data.SqlClient.Server +namespace Microsoft.SqlServer.Server { - /// + /// public enum Format { - /// + /// Unknown = 0, - /// + /// Native = 1, - /// + /// UserDefined = 2, } - /// + /// // This custom attribute indicates that the given type is // a SqlServer udt. The properties on the attribute reflect the // physical attributes that will be used when the type is registered @@ -33,10 +33,10 @@ public sealed class SqlUserDefinedTypeAttribute : Attribute private string _name; // The maximum value for the maxbytesize field, in bytes. - internal const int Sql2005MaxByteSizeValue = 8000; + internal const int YukonMaxByteSizeValue = 8000; private string _validationMethodName = null; - /// + /// // A required attribute on all udts, used to indicate that the // given type is a udt, and its storage format. public SqlUserDefinedTypeAttribute(Format format) @@ -44,17 +44,17 @@ public SqlUserDefinedTypeAttribute(Format format) switch (format) { case Format.Unknown: - throw ADP.NotSupportedUserDefinedTypeSerializationFormat(format, nameof(format)); + throw new ArgumentOutOfRangeException(typeof(Format).Name, string.Format(Strings.ADP_NotSupportedEnumerationValue, typeof(Format).Name, ((int)format).ToString(), nameof(format))); case Format.Native: case Format.UserDefined: _format = format; break; default: - throw ADP.InvalidUserDefinedTypeSerializationFormat(format); + throw new ArgumentOutOfRangeException(typeof(Format).Name, string.Format(Strings.ADP_InvalidEnumerationValue, typeof(Format).Name, ((int)format).ToString(CultureInfo.InvariantCulture))); } } - /// + /// // The maximum size of this instance, in bytes. Does not have to be // specified for Native serialization. The maximum value // for this property is specified by MaxByteSizeValue. @@ -68,13 +68,13 @@ public int MaxByteSize { if (value < -1) { - throw ADP.ArgumentOutOfRange(nameof(MaxByteSize)); + throw new ArgumentOutOfRangeException(value.ToString(), StringsHelper.GetString(Strings.SQLUDT_MaxByteSizeValue), nameof(MaxByteSize)); } _maxByteSize = value; } } - /// + /// // Are all instances of this udt the same size on disk? public bool IsFixedLength { @@ -88,7 +88,7 @@ public bool IsFixedLength } } - /// + /// // Is this type byte ordered, i.e. is the on disk representation // consistent with the ordering semantics for this type? // If true, the binary representation of the type will be used @@ -106,11 +106,11 @@ public bool IsByteOrdered } } - /// + /// // The on-disk format for this type. public Format Format => _format; - /// + /// // An Optional method used to validate this UDT public string ValidationMethodName { @@ -124,7 +124,7 @@ public string ValidationMethodName } } - /// + /// public string Name { get diff --git a/src/Microsoft.SqlServer.Server/Strings.Designer.cs b/src/Microsoft.SqlServer.Server/Strings.Designer.cs new file mode 100644 index 0000000000..ebe75538ee --- /dev/null +++ b/src/Microsoft.SqlServer.Server/Strings.Designer.cs @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.SqlServer.Server { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.SqlServer.Server.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The {0} enumeration value, {1}, is invalid.. + /// + internal static string ADP_InvalidEnumerationValue { + get { + return ResourceManager.GetString("ADP_InvalidEnumerationValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The {0} enumeration value, {1}, is not supported by the {2} method.. + /// + internal static string ADP_NotSupportedEnumerationValue { + get { + return ResourceManager.GetString("ADP_NotSupportedEnumerationValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to '{0}' is an invalid user defined type, reason: {1}.. + /// + internal static string SqlUdt_InvalidUdtMessage + { + get { + return ResourceManager.GetString("SqlUdt_InvalidUdtMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to range: 0-8000. + /// + internal static string SQLUDT_MaxByteSizeValue + { + get + { + return ResourceManager.GetString("SQLUDT_MaxByteSizeValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to no UDT attribute. + /// + internal static string SqlUdtReason_NoUdtAttribute + { + get { + return ResourceManager.GetString("SqlUdtReason_NoUdtAttribute", resourceCulture); + } + } + } +} diff --git a/src/Microsoft.SqlServer.Server/Strings.resx b/src/Microsoft.SqlServer.Server/Strings.resx new file mode 100644 index 0000000000..deb5efff24 --- /dev/null +++ b/src/Microsoft.SqlServer.Server/Strings.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The {0} enumeration value, {1}, is invalid. + + + The {0} enumeration value, {1}, is not supported by the {2} method. + + + no UDT attribute + + + '{0}' is an invalid user defined type, reason: {1}. + + + range: 0-8000 + + diff --git a/src/Microsoft.SqlServer.Server/StringsHelper.cs b/src/Microsoft.SqlServer.Server/StringsHelper.cs new file mode 100644 index 0000000000..610839adbf --- /dev/null +++ b/src/Microsoft.SqlServer.Server/StringsHelper.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Microsoft.SqlServer.Server +{ + internal class StringsHelper : Strings + { + static StringsHelper s_loader = null; + ResourceManager _resources; + + internal StringsHelper() + { + _resources = new ResourceManager("Microsoft.SqlServer.Server.Strings", GetType().Assembly); + } + + private static StringsHelper GetLoader() + { + if (s_loader == null) + { + StringsHelper sr = new(); + Interlocked.CompareExchange(ref s_loader, sr, null); + } + return s_loader; + } + + public static ResourceManager Resources => GetLoader()._resources; + + + // This method is used to decide if we need to append the exception message parameters to the message when calling Strings.Format. + // by default it returns false. + // Native code generators can replace the value this returns based on user input at the time of native code generation. + // Marked as NoInlining because if this is used in an AoT compiled app that is not compiled into a single file, the user + // could compile each module with a different setting for this. We want to make sure there's a consistent behavior + // that doesn't depend on which native module this method got inlined into. + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool UsingResourceKeys() + { + return false; + } + + public static string GetResourceString(string res) + { + StringsHelper sys = GetLoader(); + if (sys == null) + return null; + + // If "res" is a resource id, temp will not be null, "res" will contain the retrieved resource string. + // If "res" is not a resource id, temp will be null. + string temp = sys._resources.GetString(res, Culture); + if (temp != null) + res = temp; + + return res; + } + public static string GetString(string res, params object[] args) + { + res = GetResourceString(res); + if (args != null && args.Length > 0) + { + for (int i = 0; i < args.Length; i++) + { + string value = args[i] as string; + if (value != null && value.Length > 1024) + { + args[i] = value.Substring(0, 1024 - 3) + "..."; + } + } + return string.Format(CultureInfo.CurrentCulture, res, args); + } + else + { + return res; + } + } + + public static string Format(string resourceFormat, params object[] args) + { + if (args != null) + { + if (UsingResourceKeys()) + { + return resourceFormat + string.Join(", ", args); + } + + return string.Format(resourceFormat, args); + } + + return resourceFormat; + } + + public static string Format(string resourceFormat, object p1) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1); + } + + return string.Format(resourceFormat, p1); + } + + public static string Format(string resourceFormat, object p1, object p2) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2); + } + + return string.Format(resourceFormat, p1, p2); + } + + public static string Format(string resourceFormat, object p1, object p2, object p3) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2, p3); + } + + return string.Format(resourceFormat, p1, p2, p3); + } + } +} diff --git a/src/Microsoft.SqlServer.Server/TypeForwards.cs b/src/Microsoft.SqlServer.Server/TypeForwards.cs new file mode 100644 index 0000000000..8f1161bce9 --- /dev/null +++ b/src/Microsoft.SqlServer.Server/TypeForwards.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.IBinarySerialize))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.InvalidUdtException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.Format))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SqlFacetAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SqlFunctionAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SqlMethodAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.DataAccessKind))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Microsoft.SqlServer.Server.SystemDataAccessKind))] +namespace Microsoft.SqlServer.Server { } diff --git a/tools/props/Versions.props b/tools/props/Versions.props index f13965c102..23a1eb61ff 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -2,25 +2,34 @@ 1.0.0.0 - + - 4.0.0.0 + 5.0.0.0 5.0.0-dev $(NugetPackageVersion) + + + + + 1.0.0.0 + 1.0.0.0 + 1.0.0-dev + $(SqlServerPackageVersion) + - 4.0.0 + 5.0.0 4.3.1 4.3.0 - 1.3.0 - 4.22.0 - 6.8.0 - 6.8.0 + 1.6.0 + 4.45.0 + 6.21.0 + 6.21.0 4.5.1 4.3.0 4.7.2 @@ -29,8 +38,9 @@ 5.0.0 - 4.0.0 + 5.0.0 5.0.0 + 1.0.0 5.0.0 5.0.0 4.3.0 @@ -46,7 +56,7 @@ - [1.6.0,2.0.0) + [1.24.0,2.0.0) [4.0.3,5.0.0) 5.0.0 @@ -55,20 +65,20 @@ 3.1.1 5.2.6 15.9.0 - 3.1.0 13.0.1 4.3.0 4.3.0 4.5.0 4.6.0 4.3.0 - 6.8.0 + 6.21.0 2.4.1 5.0.0-beta.20206.4 2.0.8 161.41011.9 10.50.1600.1 0.12.1 + 6.0.0 $(NugetPackageVersion) diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 750ee2f48a..a9f0b2d6c7 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -27,12 +27,12 @@ When using NuGet 3.x this package requires at least version 3.4. © Microsoft Corporation. All rights reserved. sqlclient microsoft.data.sqlclient - - - - - - + + + + + + @@ -42,11 +42,12 @@ When using NuGet 3.x this package requires at least version 3.4. - - - - - + + + + + + @@ -60,11 +61,12 @@ When using NuGet 3.x this package requires at least version 3.4. - - - - - + + + + + + @@ -78,11 +80,12 @@ When using NuGet 3.x this package requires at least version 3.4. - - - - - + + + + + + @@ -97,11 +100,11 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + - + @@ -127,9 +130,9 @@ When using NuGet 3.x this package requires at least version 3.4. - - - + + + @@ -146,21 +149,21 @@ When using NuGet 3.x this package requires at least version 3.4. - - - - - - - - - - + + + + + + + + + + - - - + + + @@ -177,8 +180,8 @@ When using NuGet 3.x this package requires at least version 3.4. - - + + diff --git a/tools/specs/Microsoft.SqlServer.Server.nuspec b/tools/specs/Microsoft.SqlServer.Server.nuspec new file mode 100644 index 0000000000..fe419d58e4 --- /dev/null +++ b/tools/specs/Microsoft.SqlServer.Server.nuspec @@ -0,0 +1,50 @@ + + + + Microsoft.SqlServer.Server + 1.0.0 + Microsoft.SqlServer.Server + Microsoft + true + MIT + https://aka.ms/sqlclientproject + dotnet.png + + This is a helper library for Microsoft.Data.SqlClient enabling cross framework support of UDT types. + +Available Types: +Microsoft.SqlServer.Server.IBinarySerializer +Microsoft.SqlServer.Server.InvalidUdtException +Microsoft.SqlServer.Server.SqlFacetAttribute +Microsoft.SqlServer.Server.SqlFunctionAttribute +Microsoft.SqlServer.Server.SqlMethodAttribute +Microsoft.SqlServer.Server.SqlUserDefinedAggregateAttribute +Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute +Microsoft.SqlServer.Server.DataAccessKind +Microsoft.SqlServer.Server.SystemDataAccessKind +Microsoft.SqlServer.Server.Format + + + https://go.microsoft.com/fwlink/?linkid=2185441 + © Microsoft Corporation. All rights reserved. + sqlserver microsoft.sqlserver.server + + + + + + + + + + + + + + + + + + + + diff --git a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec index 43b996d6eb..ec14dc0276 100644 --- a/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec +++ b/tools/specs/add-ons/Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider.nuspec @@ -24,42 +24,42 @@ Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyStoreProvider.SqlColumnEncrypti © Microsoft Corporation. All rights reserved. sqlclient microsoft.data.sqlclient azurekeyvaultprovider akvprovider alwaysencrypted - + - + - + - + - - + + - - - - - + + + + + diff --git a/tools/targets/GenerateNugetPackage.targets b/tools/targets/GenerateNugetPackage.targets index 7ffecfc6b3..5ec95be2dc 100644 --- a/tools/targets/GenerateNugetPackage.targets +++ b/tools/targets/GenerateNugetPackage.targets @@ -9,4 +9,13 @@ -command "&$(ToolsDir)scripts\downloadLatestNuget.ps1 -nugetDestPath '$(NuGetRoot)'"" /> + + + $(SqlServerPackageVersion)-debug + + + + +