Skip to content

Commit b9f133b

Browse files
Merged PR 4036: [5.1.2] Fix | LocalDb and managed SNI (#2129)
Ports [#2129](#2129)
1 parent 1dba81c commit b9f133b

File tree

8 files changed

+119
-37
lines changed

8 files changed

+119
-37
lines changed

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

+8-2
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

+12-10
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

+14-7
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ internal static SNIHandle CreateConnectionHandle(
190190
case DataSource.Protocol.TCP:
191191
sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo,
192192
tlsFirst, hostNameInCertificate, serverCertificateFilename);
193-
break;
193+
break;
194194
case DataSource.Protocol.NP:
195195
sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst);
196196
break;
@@ -392,7 +392,7 @@ private static string GetLocalDBDataSource(string fullServerName, out bool error
392392
Debug.Assert(!string.IsNullOrWhiteSpace(localDBInstance), "Local DB Instance name cannot be empty.");
393393
localDBConnectionString = LocalDB.GetLocalDBConnectionString(localDBInstance);
394394

395-
if (fullServerName == null)
395+
if (fullServerName == null || string.IsNullOrEmpty(localDBConnectionString))
396396
{
397397
// The Last error is set in LocalDB.GetLocalDBConnectionString. We don't need to set Last here.
398398
error = true;
@@ -522,7 +522,18 @@ internal static string GetLocalDBInstance(string dataSource, out bool error)
522522
ReadOnlySpan<char> input = dataSource.AsSpan().TrimStart();
523523
error = false;
524524
// NetStandard 2.0 does not support passing a string to ReadOnlySpan<char>
525-
if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
525+
int index = input.IndexOf(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase);
526+
if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
527+
{
528+
instanceName = input.Trim().ToString();
529+
}
530+
else if (index > 0)
531+
{
532+
SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.ErrorLocatingServerInstance, Strings.SNI_ERROR_26);
533+
SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIProxy), EventType.ERR, "Incompatible use of prefix with LocalDb: '{0}'", dataSource);
534+
error = true;
535+
}
536+
else if (index == 0)
526537
{
527538
// When netcoreapp support for netcoreapp2.1 is dropped these slice calls could be converted to System.Range\System.Index
528539
// Such ad input = input[1..];
@@ -541,10 +552,6 @@ internal static string GetLocalDBInstance(string dataSource, out bool error)
541552
error = true;
542553
}
543554
}
544-
else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase))
545-
{
546-
instanceName = input.Trim().ToString();
547-
}
548555

549556
return instanceName;
550557
}

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

+6-9
Original file line numberDiff line numberDiff line change
@@ -1516,20 +1516,17 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
15161516
}
15171517
else
15181518
{
1519-
15201519
if (TdsParserStateObjectFactory.UseManagedSNI)
15211520
{
1522-
// SNI error. Append additional error message info if available.
1523-
//
1521+
// SNI error. Append additional error message info if available and hasn't been included.
15241522
string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber);
1525-
errorMessage = (errorMessage != string.Empty) ?
1526-
(sniLookupMessage + ": " + errorMessage) :
1527-
sniLookupMessage;
1523+
errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage))
1524+
? sniLookupMessage
1525+
: (sniLookupMessage + ": " + errorMessage);
15281526
}
15291527
else
15301528
{
15311529
// SNI error. Replace the entire message.
1532-
//
15331530
errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber);
15341531

15351532
// If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code
@@ -1538,6 +1535,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj)
15381535
errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError);
15391536
win32ErrorCode = 0;
15401537
}
1538+
SqlClientEventSource.Log.TryAdvancedTraceEvent("<sc.TdsParser.ProcessSNIError |ERR|ADV > Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage);
15411539
}
15421540
}
15431541
errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})",
@@ -12545,8 +12543,7 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar
1254512543
return true; // No data
1254612544
}
1254712545

12548-
Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
12549-
"Out of sync plp read request");
12546+
Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request");
1255012547

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

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

+20-2
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

+9-3
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>
@@ -1941,4 +1941,10 @@
19411941
<data name="SQL_TDS8_NotSupported_Netstandard2.0" xml:space="preserve">
19421942
<value>Encrypt=Strict is not supported when targeting .NET Standard 2.0. Use .NET Standard 2.1, .NET Framework, or .NET.</value>
19431943
</data>
1944-
</root>
1944+
<data name="SNI_PN10" xml:space="preserve">
1945+
<value />
1946+
</data>
1947+
<data name="SNI_PN11" xml:space="preserve">
1948+
<value>SQL Network Interfaces</value>
1949+
</data>
1950+
</root>

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,12 @@ public static bool IsAKVSetupAvailable()
345345

346346
public static bool IsNotUsingManagedSNIOnWindows() => !UseManagedSNIOnWindows;
347347

348-
public static bool IsUsingNativeSNI() => !IsUsingManagedSNI();
349-
348+
public static bool IsUsingNativeSNI() =>
349+
#if !NETFRAMEWORK
350+
DataTestUtility.IsNotUsingManagedSNIOnWindows();
351+
#else
352+
true;
353+
#endif
350354
// Synapse: UTF8 collations are not supported with Azure Synapse.
351355
// Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/40103791-utf-8-collations-should-be-supported-in-azure-syna
352356
public static bool IsUTF8Supported()

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

+44-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ private enum InfoType
1616
state
1717
}
1818
private static bool IsLocalDBEnvironmentSet() => DataTestUtility.IsLocalDBInstalled();
19+
private static bool IsNativeSNI() => DataTestUtility.IsUsingNativeSNI();
1920
private static bool IsLocalDbSharedInstanceSet() => DataTestUtility.IsLocalDbSharedInstanceSetup();
2021
private static readonly string s_localDbConnectionString = @$"server=(localdb)\{DataTestUtility.LocalDbAppName}";
2122
private static readonly string[] s_sharedLocalDbInstances = new string[] { @$"server=(localdb)\.\{DataTestUtility.LocalDbSharedInstanceName}", @$"server=(localdb)\." };
@@ -123,6 +124,47 @@ public static void LocalDBNamepipeMarsTest()
123124

124125
#endregion
125126

127+
#region Failures
128+
// ToDo: After adding shared memory support on managed SNI, the IsNativeSNI could be taken out
129+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
130+
[ConditionalTheory(nameof(IsLocalDBEnvironmentSet), nameof(IsNativeSNI))]
131+
[InlineData("lpc:")]
132+
public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix)
133+
{
134+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
135+
stringBuilder.DataSource = prefix + stringBuilder.DataSource;
136+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
137+
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);
138+
}
139+
140+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
141+
[InlineData("tcp:")]
142+
[InlineData("np:")]
143+
[InlineData("undefinded:")]
144+
[ConditionalTheory(nameof(IsLocalDBEnvironmentSet))]
145+
public static void PrefixAndSqlLocalDbConnectionTest(string prefix)
146+
{
147+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
148+
stringBuilder.DataSource = prefix + stringBuilder.DataSource;
149+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
150+
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);
151+
}
152+
153+
[SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP
154+
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))]
155+
public static void InvalidSqlLocalDbConnectionTest()
156+
{
157+
SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString);
158+
stringBuilder.DataSource = stringBuilder.DataSource + "Invalid123";
159+
SqlException ex = Assert.Throws<SqlException>(() => ConnectionTest(stringBuilder.ConnectionString));
160+
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);
161+
if (IsNativeSNI())
162+
{
163+
Assert.Contains("The specified LocalDB instance does not exist.", ex.Message);
164+
}
165+
}
166+
#endregion
167+
126168
private static void ConnectionWithMarsTest(string connectionString)
127169
{
128170
SqlConnectionStringBuilder builder = new(connectionString)
@@ -178,13 +220,13 @@ private static void RestartLocalDB()
178220
{
179221
string state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_sqlLocalDbInfo, InfoType.state);
180222
int count = 5;
181-
while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count>0)
223+
while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count > 0)
182224
{
183225
count--;
184226
state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_startLocalDbCommand, InfoType.state);
185227
Thread.Sleep(2000);
186228
}
187-
if(state == null || state != "Running")
229+
if (state == null || state != "Running")
188230
{
189231
throw new LocalDBNotStartedException();
190232
}

0 commit comments

Comments
 (0)