diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3dec07b8df..775f0d4be1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,48 @@ 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/)
+## [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:
diff --git a/build.proj b/build.proj
index 124837c2d2..14efda3f4a 100644
--- a/build.proj
+++ b/build.proj
@@ -71,7 +71,7 @@
-
+
@@ -113,7 +113,9 @@
+
+
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/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/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.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/Format.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml
deleted file mode 100644
index b15cd6eadb..0000000000
--- a/doc/snippets/Microsoft.Data.SqlClient.Server/Format.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
- 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.
-- `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.
-## 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)]
-
- ]]>
-
-
-
- This serialization format uses a very simple algorithm that enables SQL Server to store an efficient representation of the UDT on disk. Types marked for serialization can only have value types (structs in Microsoft Visual C# and structures in Microsoft Visual Basic .NET) as members. Members of reference types (such as classes in Visual C# and Visual Basic), either user-defined or those existing in .NET class libraries (such as ), are not supported.
-
-
- The serialization format is unknown.
-
-
- This serialization format gives the developer full control over the binary format through the and methods.
-
-
-
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/SqlFacetAttribute.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml
deleted file mode 100644
index bbe76cdd22..0000000000
--- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFacetAttribute.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- Annotates the returned result of a user-defined type (UDT) with additional information that can be used in Transact-SQL.
-
- 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|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|N|N|Y|
-||N|N|Y|Y|Y|
-||Y|Y|N|N|Y|
-||Y|Y|N|N|Y|
-||N|N|N|N|Y|
-||Y|Y|N|N|Y|
-||Y|Y|N|N|Y|
-|Embedded UDTs|N|N|N|N|Y|
-||Y|Y|N|N|Y|
-|Byte[]|Y|Y|N|N|Y|
-|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.
-
- ]]>
-
-
-
- An optional attribute on a user-defined type (UDT) return type, used to annotate the returned result with additional information that can be used in Transact-SQL.
- To be added.
-
-
- Indicates whether the return type of the user-defined type is of a fixed length.
-
- if the return type is of a fixed length; otherwise .
-
- property is set to 1.
-
-The default value is `false`.
-
-]]>
-
-
-
- Indicates whether the return type of the user-defined type can be .
-
- if the return type of the user-defined type can be ; otherwise .
-
-
-
-
-
- The maximum size, in logical units, of the underlying field type of the user-defined type.
- An representing the maximum size, in logical units, of the underlying field type.
-
-
-
-
-
- The precision of the return type of the user-defined type.
- An representing the precision of the return type.
-
- 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 scale of the return type of the user-defined type.
- An representing the scale of the return type.
-
- 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.Data.SqlClient.Server/SqlFunctionAttribute.xml
deleted file mode 100644
index 7221363628..0000000000
--- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlFunctionAttribute.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
-
- Used to mark a method definition of a user-defined aggregate as a function in SQL Server. The properties on the attribute reflect the physical characteristics used when the type is registered with SQL Server.
-
-
-
-
-
- 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.
-
- . 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.
-
-]]>
-
-
-
- The name of a method in the same class which is used to fill a row of data in the table returned by the table-valued function.
- A value representing the name of a method in the same class which is used to fill a row of data in the table returned by the table-valued function.
-
-
- Indicates whether the user-defined function is deterministic.
-
- if the function is deterministic; otherwise .
-
- 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.
-
-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`.
-
-Do not mark a function as deterministic if the function does not always produce the same output values, given the same input values and the same database state. Marking a function as deterministic when the function is not truly deterministic can result in corrupted indexed views and computed columns.
-
-]]>
-
-
-
- Indicates whether the function involves imprecise computations, such as floating point operations.
-
- if the function involves precise computations; otherwise .
-
-
-
-
-
- The name under which the function should be registered in SQL Server.
- A value representing the name under which the function should be registered.
-
-
-
-
-
- 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.
-
- .
-
-]]>
-
-
-
- A string that represents the table definition of the results, if the method is used as a table-valued function (TVF).
- A value representing the table definition of the results.
-
-
-
-
-
-
diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml
deleted file mode 100644
index 95f73cda49..0000000000
--- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlMethodAttribute.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
- Indicates the determinism and data access properties of a method or property on a user-defined type (UDT). The properties on the attribute reflect the physical characteristics that are used when the type is registered with SQL Server.
-
- 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.
-
-## 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)]
-
-]]>
-
-
-
- An attribute on a user-defined type (UDT), used to indicate the determinism and data access properties of a method or a property on a UDT.
- To be added.
-
-
- Indicates whether SQL Server should invoke the method on null instances.
- if SQL Server should invoke the method on null instances; otherwise, . If the method cannot be invoked (because of an attribute on the method), the SQL Server is returned.
-
-
-
-
-
- Indicates whether a method on a user-defined type (UDT) is a mutator.
- if the method is a mutator; otherwise .
-
- 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`.
-
-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`.
-
-]]>
-
-
-
- Indicates whether the method on a user-defined type (UDT) is called when input arguments are specified in the method invocation.
- if the method is called when input arguments are specified in the method invocation; if the method returns a value when any of its input parameters are . If the method cannot be invoked (because of an attribute on the method), the SQL Server is returned.
-
- property is `true`.
-
-]]>
-
-
-
-
diff --git a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml b/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml
deleted file mode 100644
index 5822bac69d..0000000000
--- a/doc/snippets/Microsoft.Data.SqlClient.Server/SqlUserDefinedAggregateAttribute.xml
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
- Indicates that the type should be registered as a user-defined aggregate. The properties on the attribute reflect the physical attributes used when the type is registered with SQL Server. This class cannot be inherited.
-
- 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.
-
-[!code-csharp[SqlUserDefinedAggregate Sample#1](~/../sqlclient/doc/samples/SqlUserDefinedAggregate.cs#1)]
-
-]]>
-
-
-
- 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.
-
-
-
-
-
- Indicates whether the aggregate is invariant to duplicates.
- if the aggregate is invariant to duplicates; otherwise .
-
-
-
-
-
- Indicates whether the aggregate is invariant to nulls.
- if the aggregate is invariant to nulls; otherwise .
-
-
-
-
-
- Indicates whether the aggregate is invariant to order.
- if the aggregate is invariant to order; otherwise .
-
-
-
-
-
- Indicates whether the aggregate returns if no values have been accumulated.
- if the aggregate returns if no values have been accumulated; otherwise .
-
-
-
-
-
- The maximum size, in bytes, of the aggregate instance.
- An value representing the maximum size of the aggregate instance.
-
- property with the UserDefined serialization .
-
-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.
-
-]]>
-
-
-
- The maximum size, in bytes, required to store the state of this aggregate instance during computation.
-
- value representing the maximum size of the aggregate instance.
-
-]]>
-
-
-
- The name of the aggregate.
- A value representing the name of the aggregate.
-
-
-
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/SqlConnection.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
index c2bf5ac5df..f444d8f2e8 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
+++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml
@@ -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|'false' in v3.1 and older 'true' in v4.0 and newer|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. 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).|
|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|Available starting in version 5.0.
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.|
|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`|
@@ -555,9 +557,10 @@ End Module
|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-
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.|
diff --git a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml
new file mode 100644
index 0000000000..acf73d2387
--- /dev/null
+++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ 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`.
+
+ ]]>
+
+
+
+ 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 72cf7aec1e..a3c12d73de 100644
--- a/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml
+++ b/doc/snippets/Microsoft.Data.SqlClient/SqlConnectionStringBuilder.xml
@@ -410,15 +410,15 @@ 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 that indicates whether TLS encryption is required for all data sent between the client and server.
+ The value of the type. or , 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`.
@@ -459,6 +459,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.
@@ -820,6 +839,25 @@ Database = AdventureWorks
]]>
+
+ 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.
@@ -898,7 +936,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/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.md b/release-notes/5.0/5.0.md
index 51c28bb3a7..16676aa278 100644
--- a/release-notes/5.0/5.0.md
+++ b/release-notes/5.0/5.0.md
@@ -4,5 +4,6 @@ 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.12 | [release notes](5.0.0-preview1.md) |
diff --git a/release-notes/5.0/README.md b/release-notes/5.0/README.md
index b0f8887bb5..3f2aa51f5e 100644
--- a/release-notes/5.0/README.md
+++ b/release-notes/5.0/README.md
@@ -2,5 +2,6 @@ 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) |
\ No newline at end of file
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 0cca6a4cb5..afcf503118 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -46,6 +46,7 @@
$(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 18cc04fbc8..c5b34945a0 100644
--- a/src/Microsoft.Data.SqlClient.sln
+++ b/src/Microsoft.Data.SqlClient.sln
@@ -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
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 12e8094ff0..deb4ad0bf9 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
@@ -383,6 +383,7 @@ public void Remove(SqlBulkCopyColumnOrderHint columnOrderHint) { }
///
public new void RemoveAt(int index) { }
}
+
///
[System.FlagsAttribute]
public enum SqlBulkCopyOptions
@@ -470,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
///
@@ -500,6 +503,33 @@ public enum SqlConnectionIPAddressPreference
///
UsePlatformDefault = 2
}
+ ///
+ public sealed class SqlConnectionEncryptOption
+ {
+ ///
+ 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
{
@@ -994,7 +1024,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)]
@@ -1003,6 +1037,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)]
@@ -1061,6 +1099,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)]
@@ -1830,37 +1872,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
{
@@ -2031,44 +2042,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
{
@@ -2207,69 +2180,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/Interop/SNINativeMethodWrapper.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs
index 4e84fdc406..dd189d178a 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Interop/SNINativeMethodWrapper.Windows.cs
@@ -19,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
@@ -43,6 +45,21 @@ 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;
+ };
+
+
internal enum ConsumerNumber
{
SNI_Consumer_SNI,
@@ -148,6 +165,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;
@@ -202,6 +221,9 @@ internal struct SNI_Error
[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);
@@ -323,12 +345,12 @@ 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);
@@ -369,9 +391,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();
@@ -380,8 +414,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 83109c32cc..77c2150b7b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -172,18 +172,12 @@
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\InvalidUdtException.cs
-
Microsoft\Data\SqlClient\Server\ITypedGetters.cs
@@ -238,29 +232,17 @@
Microsoft\Data\SqlClient\Server\SqlDataRecord.netcore.cs
-
- Microsoft\Data\SqlClient\Server\SqlFacetAttribute.cs
-
-
- Microsoft\Data\SqlClient\Server\SqlFunctionAttribute.cs
-
Microsoft\Data\SqlClient\Server\SqlMetaData.cs
-
- Microsoft\Data\SqlClient\Server\SqlMethodAttribute.cs
-
Microsoft\Data\SqlClient\Server\SqlNormalizer.cs
Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs
-
- Microsoft\Data\SqlClient\Server\SqlUserDefinedAggregateAttribute.cs
-
-
- Microsoft\Data\SqlClient\Server\SqlUserDefinedTypeAttribute.cs
+
+ Microsoft\Data\SqlClient\SqlTransaction.Common.cs
Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs
@@ -346,6 +328,9 @@
Microsoft\Data\SqlClient\SqlCommandSet.cs
+
+ Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs
+
Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs
@@ -388,6 +373,9 @@
Microsoft\Data\SqlClient\SqlEnums.cs
+
+ Microsoft\Data\SqlClient\SqlEnvChange.cs
+
Microsoft\Data\SqlClient\SqlError.cs
@@ -427,6 +415,9 @@
Microsoft\Data\SqlClient\SqlObjectPool.cs
+
+ Microsoft\Data\SqlClient\SqlParameter.cs
+
Microsoft\Data\SqlClient\SqlParameterCollection.cs
@@ -475,6 +466,9 @@
Microsoft\Data\SqlClient\TdsParameterSetter.cs
+
+ Microsoft\Data\SqlClient\TdsParserStaticMethods.cs
+
Microsoft\Data\SqlClient\TdsRecordBufferSetter.cs
@@ -499,6 +493,9 @@
Resources\StringsHelper.cs
+
+ Common\System\Diagnostics\CodeAnalysis.cs
+
@@ -562,7 +559,7 @@
-
+
@@ -614,7 +611,6 @@
-
@@ -640,7 +636,6 @@
-
@@ -649,10 +644,12 @@
-
+
+ Microsoft\Data\Common\AdapterUtil.Windows.cs
+
Microsoft\Data\Sql\SqlDataSourceEnumeratorNativeHelper.cs
@@ -668,6 +665,9 @@
+
+ Microsoft\Data\Common\AdapterUtil.Unix.cs
+
@@ -952,6 +952,9 @@
+
+
+
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..6980eb09f2 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
@@ -108,6 +108,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;
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..1d3d731e0b 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
{
@@ -15,13 +20,38 @@ internal abstract class SNIHandle
///
/// Exclude TLS 1.3 (not fully supported).
///
- 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,
+ EnabledSslProtocols = s_supportedProtocols,
+ 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 +81,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 c4ba4640f6..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, false);
- _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 501a68e401..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);
}
///
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 ad833e14be..df34f6c31b 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)
{
@@ -280,7 +300,12 @@ private Socket TryConnectParallel(string hostName, int port, TimeSpan ts, bool i
Task connectTask;
Task serverAddrTask = Dns.GetHostAddressesAsync(hostName);
- serverAddrTask.Wait(ts);
+ bool complete = serverAddrTask.Wait(ts);
+
+ // DNS timed out - don't block
+ if (!complete)
+ return null;
+
IPAddress[] serverAddresses = serverAddrTask.Result;
if (serverAddresses.Length > MaxParallelIpAddresses)
@@ -324,7 +349,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 +358,14 @@ 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);
+ Task serverAddrTask = Dns.GetHostAddressesAsync(serverName);
+ bool complete = serverAddrTask.Wait(timeout);
+
+ // DNS timed out - don't block
+ if (!complete)
+ return null;
+
+ IPAddress[] ipAddresses = serverAddrTask.Result;
string IPv4String = null;
string IPv6String = null;
@@ -581,10 +612,22 @@ public override uint EnableSsl(uint options)
using (TrySNIEventScope.Create(nameof(SNIHandle)))
{
_validateCert = (options & TdsEnums.SNI_SSL_VALIDATE_CERTIFICATE) != 0;
+
try
{
- _sslStream.AuthenticateAsClient(_targetServer, null, SupportedProtocols, false);
- _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)
{
@@ -610,7 +653,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);
@@ -631,9 +674,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);
}
///
@@ -802,14 +854,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;
}
@@ -824,10 +874,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;
}
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 d182a8d31f..ce6eb653bd 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,10 +3,13 @@
// 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
@@ -27,8 +30,11 @@ internal sealed class SSRP
///
/// 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");
@@ -38,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)
{
@@ -93,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;
@@ -137,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)))
{
@@ -152,26 +170,174 @@ 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;
-
bool isIpAddress = IPAddress.TryParse(browserHostname, out IPAddress address);
- byte[] responsePacket = null;
- using (UdpClient client = new UdpClient(!isIpAddress ? AddressFamily.InterNetwork : address.AddressFamily))
+ 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;
+ }
+
+ IPAddress[] ipAddresses = null;
+ if (!isIpAddress)
+ {
+ Task serverAddrTask = Dns.GetHostAddressesAsync(browserHostname);
+ bool taskComplete;
+ try
+ {
+ taskComplete = serverAddrTask.Wait(ts);
+ }
+ catch (AggregateException ae)
+ {
+ throw ae.InnerException;
+ }
+
+ // If DNS took too long, need to return instead of blocking
+ if (!taskComplete)
+ return null;
+
+ ipAddresses = serverAddrTask.Result;
+ }
+
+ 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)));
+ }
+
+ 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;
}
}
- return responsePacket;
}
+ catch (Exception e)
+ {
+ result.Error = e;
+ }
+
+ return result;
}
///
@@ -186,7 +352,7 @@ internal static string SendBroadcastUDPRequest()
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;
+ int currentTimeOut = FirstTimeoutForCLNT_BCAST_EX;
using (TrySNIEventScope.Create(nameof(SSRP)))
{
@@ -198,7 +364,7 @@ internal static string SendBroadcastUDPRequest()
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;
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 61b061610f..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
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/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 c15808bc8a..4ac81a9844 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUdtInfo.cs
@@ -5,11 +5,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Data.Common;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
namespace Microsoft.Data.SqlClient
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsEnums.cs
index 0cd0f13a48..956fb183c5 100644
--- a/src/Microsoft.Data.SqlClient/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
{
@@ -345,6 +347,7 @@ public enum ActiveDirectoryWorkflow : byte
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 string TDS8_Protocol = "tds/8.0"; //TDS8
// Increments:
public const int SQL2000SP1_INCREMENT = 0x00;
@@ -621,6 +624,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;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs
index b3fb0a568e..8d2716c63e 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserSafeHandles.Windows.cs
@@ -161,7 +161,9 @@ internal SNIHandle(
int totalTimeout,
#endif
SqlConnectionIPAddressPreference ipPreference,
- SQLDNSInfo cachedDNSInfo)
+ SQLDNSInfo cachedDNSInfo,
+ bool tlsFirst,
+ string hostNameInCertificate)
: base(IntPtr.Zero, true)
{
RuntimeHelpers.PrepareConstrainedRegions();
@@ -183,10 +185,10 @@ 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);
#else
_status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle,
- spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, ipPreference, cachedDNSInfo);
+ spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, ipPreference, cachedDNSInfo, hostNameInCertificate);
#endif // NETFRAMEWORK
}
}
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 67%
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 7be7f61bb4..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
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/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/Microsoft.Data.SqlClient.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.Tests.csproj
index 721d8a5c91..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
@@ -97,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..65e0a5d32f 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,62 @@ 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);
+ }
+
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 99bfc55140..77546de525 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlDataRecordTest.cs
@@ -390,10 +390,6 @@ IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
}
}
-#if NETFRAMEWORK
[SqlServer.Server.SqlUserDefinedType(SqlServer.Server.Format.UserDefined)]
-#else
- [SqlUserDefinedType(Format.UserDefined)]
-#endif
public class TestUdt {}
}
diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs
index d37e20667d..d57cf853e9 100644
--- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlFacetAttributeTest.cs
@@ -2,11 +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.
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
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 8d3c46b75a..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);
@@ -715,14 +720,10 @@ public void UdtConstructorTest()
[Fact]
public static void InvalidUdtEcxeption_Throws()
{
- var e = Assert.Throws
-#if NETFRAMEWORK
-
-#else
-
-#endif
- (() => 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);
+ 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]
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/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
index b6d2f73532..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
{
@@ -296,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()
@@ -430,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();
@@ -438,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();
@@ -446,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.
///
@@ -459,6 +481,7 @@ 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();
}
@@ -828,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;
@@ -867,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 14c95c74d0..aef505ed1b 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
@@ -272,6 +272,7 @@
+
@@ -301,6 +302,7 @@
+
@@ -319,7 +321,6 @@
-
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..32115b96d1 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
@@ -368,5 +371,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/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/InstanceNameTest/InstanceNameTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/InstanceNameTest/InstanceNameTest.cs
index 3202636c3c..1c1869f7f1 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,9 @@
// 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.Sockets;
+using System.Text;
using System.Threading.Tasks;
using Xunit;
@@ -13,7 +15,7 @@ public static class InstanceNameTest
[ConditionalFact(typeof(DataTestUtility), 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:", "");
@@ -26,24 +28,69 @@ public static void ConnectToSQLWithInstanceNameTest()
if (proceed)
{
- using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
- {
- connection.Open();
- connection.Close();
- }
+ using SqlConnection connection = new(builder.ConnectionString);
+ connection.Open();
+ connection.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 +103,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 ada66a79ff..0c06e6cf47 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs
@@ -140,7 +140,7 @@ private static void ConnectionWithEncryptionTest(string connectionString)
{
IntegratedSecurity = true,
ConnectTimeout = 2,
- Encrypt = true
+ Encrypt = SqlConnectionEncryptOption.Mandatory
};
OpenConnection(builder.ConnectionString);
}
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 a213a8e148..49cb91792e 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs
@@ -708,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/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/UdtTest/UDTs/Address/Address.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UDTs/Address/Address.cs
index 01057ebb2e..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,11 +5,7 @@
using System;
using System.Data.SqlTypes;
using System.IO;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
[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 0964e14847..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,11 +6,7 @@
using System.Data.SqlTypes;
using System.IO;
using System.Text;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
[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 5392e57818..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,11 +6,7 @@
using System.Data.SqlTypes;
using System.IO;
using System.Text;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
[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 addc6e266f..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,11 +6,7 @@
using System.Data.SqlTypes;
using System.IO;
using System.Text;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
[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 b1b7a4ea7f..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,11 +6,7 @@
using System.Data.SqlTypes;
using System.IO;
using System.Text;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
[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 4bba8c88ba..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,11 +5,7 @@
using System;
using System.Data.SqlTypes;
using System.Globalization;
-#if NETFRAMEWORK
using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
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 e0e30dd2e5..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,12 +6,8 @@
using System.Data;
using System.Data.SqlTypes;
using System.Text;
-#if NETFRAMEWORK
-using Microsoft.SqlServer.Server;
-#else
-using Microsoft.Data.SqlClient.Server;
-#endif
using Xunit;
+using Microsoft.SqlServer.Server;
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
@@ -574,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/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/tools/props/Versions.props b/tools/props/Versions.props
index 57ca8239fd..ca8f41ae56 100644
--- a/tools/props/Versions.props
+++ b/tools/props/Versions.props
@@ -20,14 +20,14 @@
- 5.0.0-preview2.22084.1
+ 5.0.0-preview3.22165.14.3.14.3.0
- 1.5.0
- 4.30.1
+ 1.6.0
+ 4.43.26.8.06.8.04.5.1
@@ -38,8 +38,9 @@
5.0.0
- 5.0.0-preview2.22084.1
+ 5.0.0-preview3.22165.15.0.0
+ 1.0.05.0.05.0.04.3.0
@@ -55,7 +56,7 @@
- [1.20.0,2.0.0)
+ [1.24.0,2.0.0)[4.0.3,5.0.0)5.0.0
@@ -64,7 +65,6 @@
3.1.15.2.615.9.0
- 3.1.013.0.14.3.04.3.0
diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec
index 631922575e..b4412f3e75 100644
--- a/tools/specs/Microsoft.Data.SqlClient.nuspec
+++ b/tools/specs/Microsoft.Data.SqlClient.nuspec
@@ -28,9 +28,9 @@ When using NuGet 3.x this package requires at least version 3.4.
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.
-
-
-
+
+
+
+