Skip to content

Commit 34be57d

Browse files
committed
Merge branch 'main' into merge/tdsparserstateobject-lifetimes
2 parents ab27513 + 85f00ea commit 34be57d

34 files changed

+958
-427
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ insert_final_newline = true
1111
indent_style = space
1212
indent_size = 4
1313

14-
[project.json]
14+
[*.{json,jsonc}]
1515
indent_size = 2
1616

1717
# C# files

doc/samples/SqlConnection_AccessTokenCallback.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using System;
21
// <Snippet1>
2+
using System;
33
using System.Collections.Concurrent;
44
using System.Threading;
55
using System.Threading.Tasks;
@@ -17,10 +17,10 @@ static void Main()
1717

1818
const string defaultScopeSuffix = "/.default";
1919

20-
// Reuse credential objects to take advantage of underlying token caches
20+
// Reuse credential objects to take advantage of underlying token caches.
2121
private static ConcurrentDictionary<string, DefaultAzureCredential> credentials = new ConcurrentDictionary<string, DefaultAzureCredential>();
2222

23-
// Use a shared callback function for connections that should be in the same connection pool
23+
// Use a shared callback function for connections that should be in the same connection pool.
2424
private static Func<SqlAuthenticationParameters, CancellationToken, Task<SqlAuthenticationToken>> myAccessTokenCallback =
2525
async (authParams, cancellationToken) =>
2626
{
@@ -31,7 +31,7 @@ static void Main()
3131
DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions();
3232
options.ManagedIdentityClientId = authParams.UserId;
3333

34-
// Reuse the same credential object if we are using the same MI Client Id
34+
// Reuse the same credential object if we are using the same MI Client Id.
3535
AccessToken token = await credentials.GetOrAdd(authParams.UserId, new DefaultAzureCredential(options)).GetTokenAsync(
3636
new TokenRequestContext(new string[] { scope }),
3737
cancellationToken);

doc/snippets/Microsoft.Data.SqlClient/SqlConnection.xml

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,35 @@ The following example creates a <xref:Microsoft.Data.SqlClient.SqlCommand> and a
236236
<value>
237237
The access token for the connection.
238238
</value>
239+
<exception cref="T:System.InvalidOperationException">
240+
The <see cref="P:Microsoft.Data.SqlClient.SqlConnection.AccessToken" />
241+
is combined with other conflicting authentication configurations.
242+
</exception>
239243
<remarks>
240-
The AccessToken is a part of the connection pool key. Care should be taken when using this property to manage your own access token. The application is responsible for knowing when the token expires and connections from the pool should no longer be used. If you set a minimum pool size > 0 along with AccessToken, you must call <see cref="M:Microsoft.Data.SqlClient.SqlConnection.ClearPool(Microsoft.Data.SqlClient.SqlConnection)" /> after the access token expires to ensure the connection pool doesn't maintain those connections indefinitely.
244+
<para>
245+
The AccessToken is a part of the connection pool key. Supplying a
246+
refreshed access token to a new connection, with all other properties
247+
the same as other existing connections, will place that new connection
248+
into a new pool. Care should be taken when using this property to
249+
manage your own access token. Consider using the
250+
<see cref="P:Microsoft.Data.SqlClient.SqlConnection.AccessTokenCallback" />
251+
property instead, which will not consider refreshed access tokens as
252+
distinct for the purposes of connection pooling.
253+
</para>
254+
<para>
255+
The application is responsible for knowing when the access token
256+
expires and the connections from the pool should no longer be used. If
257+
you set a minimum pool size &gt; 0 along with an AccessToken, you must
258+
call
259+
<see cref="M:Microsoft.Data.SqlClient.SqlConnection.ClearPool(Microsoft.Data.SqlClient.SqlConnection)" />
260+
after the access token expires to ensure the connection pool doesn't
261+
maintain the physical connections (created by the pool) indefinitely.
262+
</para>
263+
<para>
264+
This property is mutually exclusive with the
265+
<see cref="P:Microsoft.Data.SqlClient.SqlConnection.AccessTokenCallback" />
266+
property, among others.
267+
</para>
241268
</remarks>
242269
</AccessToken>
243270
<AccessTokenCallback>
@@ -271,10 +298,10 @@ The following example creates a <xref:Microsoft.Data.SqlClient.SqlCommand> and a
271298

272299
const string defaultScopeSuffix = "/.default";
273300

274-
// Reuse credential objects to take advantage of underlying token caches
301+
// Reuse credential objects to take advantage of underlying token caches.
275302
private static ConcurrentDictionar&lt;string, DefaultAzureCredential&gt; credentials = new ConcurrentDictionary&lt;string, DefaultAzureCredential&gt;();
276303

277-
// Use a shared callback function for connections that should be in the same connection pool
304+
// Use a shared callback function for connections that should be in the same connection pool.
278305
private static Func&lt;SqlAuthenticationParameters, CancellationToken, Task&lt;SqlAuthenticationToken&gt;&gt; myAccessTokenCallback =
279306
async (authParams, cancellationToken) =>
280307
{
@@ -285,7 +312,7 @@ The following example creates a <xref:Microsoft.Data.SqlClient.SqlCommand> and a
285312
DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions();
286313
options.ManagedIdentityClientId = authParams.UserId;
287314

288-
// Reuse the same credential object if we are using the same MI Client ID
315+
// Reuse the same credential object if we are using the same MI Client ID.
289316
AccessToken token = await credentials.GetOrAdd(authParams.UserId, new DefaultAzureCredential(options)).GetTokenAsync(
290317
new TokenRequestContext(new string[] { scope }),
291318
cancellationToken);
@@ -317,6 +344,26 @@ The following example creates a <xref:Microsoft.Data.SqlClient.SqlCommand> and a
317344
<exception cref="T:System.InvalidOperationException">
318345
The <see cref="P:Microsoft.Data.SqlClient.SqlConnection.AccessTokenCallback" /> is combined with other conflicting authentication configurations.
319346
</exception>
347+
<remarks>
348+
<para>
349+
The AccessTokenCallback function itself is a part of the connection
350+
pool key. The same callback function should always provide a valid
351+
access token of the same security context given the same
352+
SqlAuthenticationParameters. When multiple connections use the same
353+
callback function, and all other properties that comprise the pool key
354+
are the same, they will be grouped into the same connection pool.
355+
</para>
356+
<para>
357+
When using a token callback function, the connection manages
358+
refreshing the tokens returned by the callback. The application is
359+
not responsible for knowing when tokens expire.
360+
</para>
361+
<para>
362+
This property is mutually exclusive with the
363+
<see cref="P:Microsoft.Data.SqlClient.SqlConnection.AccessToken" />
364+
property, among others.
365+
</para>
366+
</remarks>
320367
</AccessTokenCallback>
321368
<BeginDbTransaction>
322369
<param name="isolationLevel">

spec/user-agent-v1.jsonc

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
// This is the JSON Schema that defines the format of the TDS USERAGENT
3+
// Feature Extension payload (version 1) sent to the server during login.
4+
//
5+
// Feature Extension Name: USERAGENT
6+
// Feature Extension Version: 1
7+
// Schema Version: 1
8+
//
9+
// The design document for version 1 is here:
10+
//
11+
// https://microsoft.sharepoint-df.com/:w:/t/sqldevx/ERIWTt0zlCxLroNHyaPlKYwBI_LNSff6iy_wXZ8xX6nctQ?e=iP8q75
12+
13+
"$schema": "https://json-schema.org/draft/2020-12/schema",
14+
"$id": "urn:microsoft:mssql:user-agent:v1",
15+
"title": "Driver User Agent V1",
16+
"description": "The user agent payload sent by the driver during login.",
17+
18+
"type": "object",
19+
"properties":
20+
{
21+
"driver":
22+
{
23+
"enum": ["MS-JDBC", "MS-MDS", "MS-ODBC", "MS-OLEDB", "MS-PHP", "MS-PYTHON"],
24+
"description": "The type of driver."
25+
},
26+
"version":
27+
{
28+
"type": "string",
29+
"description": "The version of the driver, as a semantic version.",
30+
// See:
31+
//
32+
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
33+
//
34+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
35+
},
36+
"os":
37+
{
38+
"type": "object",
39+
"description": "Information about the operating system.",
40+
"properties":
41+
{
42+
"type":
43+
{
44+
"enum": ["Windows", "Linux", "macOS", "FreeBSD", "Android", "Unknown"],
45+
"description": "The type of operating system."
46+
},
47+
"details":
48+
{
49+
"type": "string",
50+
"description": "Extended details of the operating system."
51+
}
52+
},
53+
"additionalProperties": false,
54+
"required": ["type", "details"]
55+
},
56+
"arch":
57+
{
58+
"type": "string",
59+
"description": "The architecture of the driver process."
60+
},
61+
"runtime":
62+
{
63+
"type": "string",
64+
"description": "The runtime environment of the driver."
65+
}
66+
},
67+
"additionalProperties": false,
68+
"required": ["driver", "version", "os", "arch", "runtime"],
69+
"examples":
70+
[
71+
{
72+
"driver": "MS-MDS",
73+
"version": "6.0.2",
74+
"os":
75+
{
76+
"type": "Linux",
77+
"details": "Debian GNU Linux 12.2 Bookworm"
78+
},
79+
"arch": "amd64",
80+
"runtime": ".NET 9.0.3"
81+
},
82+
{
83+
"driver": "MS-JDBC",
84+
"version": "11.2.0",
85+
"os":
86+
{
87+
"type": "Windows",
88+
"details": "Windows 10 Pro 22H2"
89+
},
90+
"arch": "x64",
91+
"runtime": "Java 17.0.8+7-LTS"
92+
}
93+
]
94+
}

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\ConnectionString\AttestationProtocolUtilities.cs">
5454
<Link>Microsoft\Data\Common\ConnectionString\AttestationProtocolUtilities.cs</Link>
5555
</Compile>
56+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\ConnectionString\DbConnectionOptions.cs">
57+
<Link>Microsoft\Data\Common\ConnectionString\DbConnectionOptions.cs</Link>
58+
</Compile>
59+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\ConnectionString\DbConnectionOptions.Debug.cs">
60+
<Link>Microsoft\Data\Common\ConnectionString\DbConnectionOptions.Debug.cs</Link>
61+
</Compile>
5662
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\ConnectionString\DbConnectionStringDefaults.cs">
5763
<Link>Microsoft\Data\Common\ConnectionString\DbConnectionStringDefaults.cs</Link>
5864
</Compile>
@@ -71,9 +77,6 @@
7177
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\ConnectionString\PoolBlockingUtilities.cs">
7278
<Link>Microsoft\Data\Common\ConnectionString\PoolBlockingUtilities.cs</Link>
7379
</Compile>
74-
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\DbConnectionOptions.Common.cs">
75-
<Link>Microsoft\Data\Common\DbConnectionOptions.Common.cs</Link>
76-
</Compile>
7780
<Compile Include="$(CommonSourceRoot)Microsoft\Data\Common\MultipartIdentifier.cs">
7881
<Link>Microsoft\Data\Common\MultipartIdentifier.cs</Link>
7982
</Compile>
@@ -728,7 +731,8 @@
728731
<Compile Include="$(CommonSourceRoot)System\Diagnostics\CodeAnalysis.cs">
729732
<Link>System\Diagnostics\CodeAnalysis.cs</Link>
730733
</Compile>
731-
<Compile Include="Microsoft\Data\Common\DbConnectionOptions.cs" />
734+
735+
<Compile Include="Microsoft\Data\Common\ConnectionString\DbConnectionOptions.netcore.cs" />
732736
<Compile Include="Microsoft\Data\SqlClient\SNI\ConcurrentQueueSemaphore.cs" />
733737
<Compile Include="Microsoft\Data\SqlClient\SNI\SNIError.cs" />
734738
<Compile Include="Microsoft\Data\SqlClient\SNI\SNICommon.cs" />

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/DbConnectionOptions.cs renamed to src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/Common/ConnectionString/DbConnectionOptions.netcore.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
using System;
66
using System.IO;
77
using System.Text;
8-
using Microsoft.Data.Common.ConnectionString;
98

10-
namespace Microsoft.Data.Common
9+
namespace Microsoft.Data.Common.ConnectionString
1110
{
1211
internal partial class DbConnectionOptions
1312
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
3535
{
3636
private static int _objectTypeCount; // EventSource Counter
3737
private const int MaxRPCNameLength = 1046;
38-
internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText;
38+
internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount);
3939

4040
internal sealed class ExecuteReaderAsyncCallContext : AAsyncCallContext<SqlCommand, SqlDataReader, CancellationTokenRegistration>
4141
{
@@ -113,6 +113,7 @@ protected override void AfterCleared(SqlCommand owner)
113113
}
114114
}
115115

116+
private string _commandText;
116117
private CommandType _commandType;
117118
private int? _commandTimeout;
118119
private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
@@ -2794,7 +2795,7 @@ private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior,
27942795
}
27952796

27962797
source.SetException(e);
2797-
context.Dispose();
2798+
context?.Dispose();
27982799
}
27992800

28002801
return returnedTask;
@@ -3051,7 +3052,6 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
30513052
}
30523053
context.Set(this, source, registration, operationId);
30533054

3054-
30553055
Task<XmlReader> returnedTask = source.Task;
30563056
try
30573057
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.Threading;
1919
using System.Threading.Tasks;
2020
using Microsoft.Data.Common;
21+
using Microsoft.Data.Common.ConnectionString;
2122
using Microsoft.Data.ProviderBase;
2223
using Microsoft.Data.SqlClient.ConnectionPool;
2324
using Microsoft.Data.SqlClient.Diagnostics;

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.IO;
99
using Microsoft.Data.Common;
10+
using Microsoft.Data.Common.ConnectionString;
1011
using Microsoft.Data.ProviderBase;
1112
using Microsoft.Data.SqlClient.ConnectionPool;
1213

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlConnectionHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
using System.Threading;
1010
using System.Transactions;
1111
using Microsoft.Data.Common;
12+
using Microsoft.Data.Common.ConnectionString;
1213
using Microsoft.Data.ProviderBase;
1314
using Microsoft.Data.SqlClient.ConnectionPool;
1415

15-
1616
namespace Microsoft.Data.SqlClient
1717
{
1818
public sealed partial class SqlConnection : DbConnection

0 commit comments

Comments
 (0)