Skip to content

Commit f96f29e

Browse files
Merged PR 4063: [4.0.4] Fix | LocalDb and managed SNI (#2129)
Ports [#2129](#2129)
1 parent 4baf065 commit f96f29e

File tree

8 files changed

+115
-33
lines changed

8 files changed

+115
-33
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ private string GetConnectionString(string localDbInstance)
5050
{
5151
StringBuilder localDBConnectionString = new StringBuilder(MAX_LOCAL_DB_CONNECTION_STRING_SIZE + 1);
5252
int sizeOfbuffer = localDBConnectionString.Capacity;
53-
localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer);
54-
return localDBConnectionString.ToString();
53+
int result = localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer);
54+
if (result != TdsEnums.SNI_SUCCESS)
55+
{
56+
SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBErrorCode, Strings.SNI_ERROR_50);
57+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Unsuccessful 'LocalDBStartInstance' method call with {0} result to start '{1}' localDb instance", args0: result, args1: localDbInstance);
58+
localDBConnectionString = null;
59+
}
60+
return localDBConnectionString?.ToString();
5561
}
5662

5763
internal enum LocalDBErrorState

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@ namespace Microsoft.Data.SqlClient.SNI
2121
/// </summary>
2222
internal enum SNIProviders
2323
{
24-
HTTP_PROV, // HTTP Provider
25-
NP_PROV, // Named Pipes Provider
26-
SESSION_PROV, // Session Provider
27-
SIGN_PROV, // Sign Provider
28-
SM_PROV, // Shared Memory Provider
29-
SMUX_PROV, // SMUX Provider
30-
SSL_PROV, // SSL Provider
31-
TCP_PROV, // TCP Provider
32-
MAX_PROVS, // Number of providers
33-
INVALID_PROV // SQL Network Interfaces
24+
HTTP_PROV = 0, // HTTP Provider
25+
NP_PROV = 1, // Named Pipes Provider
26+
SESSION_PROV = 2, // Session Provider
27+
SIGN_PROV = 3, // Sign Provider
28+
SM_PROV = 4, // Shared Memory Provider
29+
SMUX_PROV = 5, // SMUX Provider
30+
SSL_PROV = 6, // SSL Provider
31+
TCP_PROV = 7, // TCP Provider
32+
VIA_PROV = 8, // Virtual Interface Architecture Provider
33+
CTAIP_PROV = 9,
34+
MAX_PROVS = 10, // Number of providers
35+
INVALID_PROV = 11 // SQL Network Interfaces
3436
}
3537

3638
/// <summary>

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNIProxy.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ private static string GetLocalDBDataSource(string fullServerName, out bool error
356356
Debug.Assert(!string.IsNullOrWhiteSpace(localDBInstance), "Local DB Instance name cannot be empty.");
357357
localDBConnectionString = LocalDB.GetLocalDBConnectionString(localDBInstance);
358358

359-
if (fullServerName == null)
359+
if (fullServerName == null || string.IsNullOrEmpty(localDBConnectionString))
360360
{
361361
// The Last error is set in LocalDB.GetLocalDBConnectionString. We don't need to set Last here.
362362
error = true;
@@ -486,7 +486,18 @@ internal static string GetLocalDBInstance(string dataSource, out bool error)
486486
ReadOnlySpan<char> input = dataSource.AsSpan().TrimStart();
487487
error = false;
488488
// NetStandard 2.0 does not support passing a string to ReadOnlySpan<char>
489-
if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
489+
int index = input.IndexOf(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase);
490+
if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
491+
{
492+
instanceName = input.Trim().ToString();
493+
}
494+
else if (index > 0)
495+
{
496+
SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.ErrorLocatingServerInstance, Strings.SNI_ERROR_26);
497+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIProxy), EventType.ERR, "Incompatible use of prefix with LocalDb: '{0}'", dataSource);
498+
error = true;
499+
}
500+
else if (index == 0)
490501
{
491502
// When netcoreapp support for netcoreapp2.1 is dropped these slice calls could be converted to System.Range\System.Index
492503
// Such ad input = input[1..];
@@ -505,10 +516,6 @@ internal static string GetLocalDBInstance(string dataSource, out bool error)
505516
error = true;
506517
}
507518
}
508-
else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
509-
{
510-
instanceName = input.Trim().ToString();
511-
}
512519

513520
return instanceName;
514521
}

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,20 +1434,17 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14341434
}
14351435
else
14361436
{
1437-
14381437
if (TdsParserStateObjectFactory.UseManagedSNI)
14391438
{
1440-
// SNI error. Append additional error message info if available.
1441-
//
1439+
// SNI error. Append additional error message info if available and hasn't been included.
14421440
string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber);
1443-
errorMessage = (errorMessage != string.Empty) ?
1444-
(sniLookupMessage + ": " + errorMessage) :
1445-
sniLookupMessage;
1441+
errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage))
1442+
? sniLookupMessage
1443+
: (sniLookupMessage + ": " + errorMessage);
14461444
}
14471445
else
14481446
{
14491447
// SNI error. Replace the entire message.
1450-
//
14511448
errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber);
14521449

14531450
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
@@ -1456,6 +1453,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
14561453
errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError);
14571454
win32ErrorCode = 0;
14581455
}
1456+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage);
14591457
}
14601458
}
14611459
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
@@ -12459,8 +12457,7 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar
1245912457
return true; // No data
1246012458
}
1246112459

12462-
Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
12463-
"Out of sync plp read request");
12460+
Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request");
1246412461

1246512462
Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
1246612463
charsLeft = len;

src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs

Lines changed: 20 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,10 +1195,10 @@
11951195
<value>TCP Provider</value>
11961196
</data>
11971197
<data name="SNI_PN8" xml:space="preserve">
1198-
<value />
1198+
<value>VIA Provider</value>
11991199
</data>
12001200
<data name="SNI_PN9" xml:space="preserve">
1201-
<value>SQL Network Interfaces</value>
1201+
<value>CTAIP Provider</value>
12021202
</data>
12031203
<data name="AZURESQL_GenericEndpoint" xml:space="preserve">
12041204
<value>.database.windows.net</value>
@@ -1935,4 +1935,10 @@
19351935
<data name="AAD_Token_Retrieving_Timeout" xml:space="preserve">
19361936
<value>Connection timed out while retrieving an access token using '{0}' authentication method. Last error: {1}: {2}</value>
19371937
</data>
1938+
<data name="SNI_PN10" xml:space="preserve">
1939+
<value />
1940+
</data>
1941+
<data name="SNI_PN11" xml:space="preserve">
1942+
<value>SQL Network Interfaces</value>
1943+
</data>
19381944
</root>

src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,12 @@ public static bool IsAKVSetupAvailable()
327327

328328
public static bool IsNotUsingManagedSNIOnWindows() => !UseManagedSNIOnWindows;
329329

330-
public static bool IsUsingNativeSNI() => !IsUsingManagedSNI();
331-
330+
public static bool IsUsingNativeSNI() =>
331+
#if !NETFRAMEWORK
332+
DataTestUtility.IsNotUsingManagedSNIOnWindows();
333+
#else
334+
true;
335+
#endif
332336
// Synapse: UTF8 collations are not supported with Azure Synapse.
333337
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/40103791-utf-8-collations-should-be-supported-in-azure-syna
334338
public static bool IsUTF8Supported()

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Microsoft.Data.SqlClient.ManualTesting.Tests
1111
public static class LocalDBTest
1212
{
1313
private static bool IsLocalDBEnvironmentSet() => DataTestUtility.IsLocalDBInstalled();
14+
private static bool IsNativeSNI() => DataTestUtility.IsUsingNativeSNI();
1415
private static bool IsLocalDbSharedInstanceSet() => DataTestUtility.IsLocalDbSharedInstanceSetup();
1516
private static readonly string s_localDbConnectionString = @$"server=(localdb)\{DataTestUtility.LocalDbAppName}";
1617
private static readonly string[] s_sharedLocalDbInstances = new string[] { @$"server=(localdb)\.\{DataTestUtility.LocalDbSharedInstanceName}", @$"server=(localdb)\." };
@@ -90,6 +91,47 @@ public static void SqlLocalDbSharedInstanceConnectionTest()
9091
}
9192
#endregion
9293

94+
#region Failures
95+
// ToDo: After adding shared memory support on managed SNI, the IsNativeSNI could be taken out
96+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
97+
[ConditionalTheory(nameof(IsLocalDBEnvironmentSet), nameof(IsNativeSNI))]
98+
[InlineData("lpc:")]
99+
public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix)
100+
{
101+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
102+
stringBuilder.DataSource = prefix + stringBuilder.DataSource;
103+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
104+
Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 41 - Cannot open a Shared Memory connection to a remote SQL server)", ex.Message);
105+
}
106+
107+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
108+
[InlineData("tcp:")]
109+
[InlineData("np:")]
110+
[InlineData("undefinded:")]
111+
[ConditionalTheory(nameof(IsLocalDBEnvironmentSet))]
112+
public static void PrefixAndSqlLocalDbConnectionTest(string prefix)
113+
{
114+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
115+
stringBuilder.DataSource = prefix + stringBuilder.DataSource;
116+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
117+
Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)", ex.Message);
118+
}
119+
120+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
121+
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))]
122+
public static void InvalidSqlLocalDbConnectionTest()
123+
{
124+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
125+
stringBuilder.DataSource = stringBuilder.DataSource + "Invalid123";
126+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
127+
Assert.Contains("A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 50 - Local Database Runtime error occurred.", ex.Message);
128+
if (IsNativeSNI())
129+
{
130+
Assert.Contains("The specified LocalDB instance does not exist.", ex.Message);
131+
}
132+
}
133+
#endregion
134+
93135
private static void ConnectionWithMarsTest(string connectionString)
94136
{
95137
SqlConnectionStringBuilder builder = new(connectionString)

0 commit comments

Comments
 (0)