From 7622824d04f33c411ab749bf6ac4763d72e27d62 Mon Sep 17 00:00:00 2001 From: Davoud Date: Fri, 18 Aug 2023 13:26:55 -0700 Subject: [PATCH 1/2] fix --- .../Data/SqlClient/SNI/LocalDB.Windows.cs | 10 +++- .../Microsoft/Data/SqlClient/SNI/SNICommon.cs | 22 +++++---- .../Microsoft/Data/SqlClient/SNI/SNIProxy.cs | 21 ++++++--- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 14 +++--- .../ManualTests/DataCommon/DataTestUtility.cs | 10 ++-- .../SQL/LocalDBTest/LocalDBTest.cs | 46 ++++++++++++++++++- 6 files changed, 91 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs index 68eaa25486..152f016061 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs @@ -50,8 +50,14 @@ private string GetConnectionString(string localDbInstance) { StringBuilder localDBConnectionString = new StringBuilder(MAX_LOCAL_DB_CONNECTION_STRING_SIZE + 1); int sizeOfbuffer = localDBConnectionString.Capacity; - localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer); - return localDBConnectionString.ToString(); + int result = localDBStartInstanceFunc(localDbInstance, 0, localDBConnectionString, ref sizeOfbuffer); + if (result != TdsEnums.SNI_SUCCESS) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBErrorCode, Strings.SNI_ERROR_50); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Unsuccessful 'LocalDBStartInstance' method call with {0} result to start '{1}' localDb instance", result, localDbInstance); + localDBConnectionString = null; + } + return localDBConnectionString?.ToString(); } internal enum LocalDBErrorState 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 b92746098f..08272596d8 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 @@ -21,16 +21,18 @@ namespace Microsoft.Data.SqlClient.SNI /// internal enum SNIProviders { - HTTP_PROV, // HTTP Provider - NP_PROV, // Named Pipes Provider - SESSION_PROV, // Session Provider - SIGN_PROV, // Sign Provider - SM_PROV, // Shared Memory Provider - SMUX_PROV, // SMUX Provider - SSL_PROV, // SSL Provider - TCP_PROV, // TCP Provider - MAX_PROVS, // Number of providers - INVALID_PROV // SQL Network Interfaces + HTTP_PROV = 0, // HTTP Provider + NP_PROV = 1, // Named Pipes Provider + SESSION_PROV = 2, // Session Provider + SIGN_PROV = 3, // Sign Provider + SM_PROV = 4, // Shared Memory Provider + SMUX_PROV = 5, // SMUX Provider + SSL_PROV = 6, // SSL Provider + TCP_PROV = 7, // TCP Provider + VIA_PROV = 8, // Virtual Interface Architecture Provider + CTAIP_PROV = 9, + MAX_PROVS = 10, // Number of providers + INVALID_PROV = 11 // SQL Network Interfaces } /// 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 8f101b7bdf..42ece11b93 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 @@ -188,7 +188,7 @@ internal static SNIHandle CreateConnectionHandle( case DataSource.Protocol.TCP: sniHandle = CreateTcpHandle(details, timerExpire, parallel, ipPreference, cachedFQDN, ref pendingDNSInfo, tlsFirst, hostNameInCertificate, serverCertificateFilename); - break; + break; case DataSource.Protocol.NP: sniHandle = CreateNpHandle(details, timerExpire, parallel, tlsFirst); break; @@ -390,7 +390,7 @@ private static string GetLocalDBDataSource(string fullServerName, out bool error Debug.Assert(!string.IsNullOrWhiteSpace(localDBInstance), "Local DB Instance name cannot be empty."); localDBConnectionString = LocalDB.GetLocalDBConnectionString(localDBInstance); - if (fullServerName == null) + if (fullServerName == null || string.IsNullOrEmpty(localDBConnectionString)) { // The Last error is set in LocalDB.GetLocalDBConnectionString. We don't need to set Last here. error = true; @@ -520,7 +520,18 @@ internal static string GetLocalDBInstance(string dataSource, out bool error) ReadOnlySpan input = dataSource.AsSpan().TrimStart(); error = false; // NetStandard 2.0 does not support passing a string to ReadOnlySpan - if (input.StartsWith(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + int index = input.IndexOf(LocalDbHost.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase); + if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) + { + instanceName = input.Trim().ToString(); + } + else if (index > 0) + { + SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.ErrorLocatingServerInstance, Strings.SNI_ERROR_26); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(SNIProxy), EventType.ERR, "Incompatible use of prefix with LocalDb: '{0}'", dataSource); + error = true; + } + else if (index == 0) { // When netcoreapp support for netcoreapp2.1 is dropped these slice calls could be converted to System.Range\System.Index // Such ad input = input[1..]; @@ -539,10 +550,6 @@ internal static string GetLocalDBInstance(string dataSource, out bool error) error = true; } } - else if (input.StartsWith(LocalDbHost_NP.AsSpan().Trim(), StringComparison.InvariantCultureIgnoreCase)) - { - instanceName = input.Trim().ToString(); - } return instanceName; } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 8c16690149..97dd4fbb31 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -1514,20 +1514,17 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) } else { - if (TdsParserStateObjectFactory.UseManagedSNI) { - // SNI error. Append additional error message info if available. - // + // SNI error. Append additional error message info if available and hasn't been included. string sniLookupMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); - errorMessage = (errorMessage != string.Empty) ? - (sniLookupMessage + ": " + errorMessage) : - sniLookupMessage; + errorMessage = (string.IsNullOrEmpty(errorMessage) || errorMessage.Contains(sniLookupMessage)) + ? sniLookupMessage + : (sniLookupMessage + ": " + errorMessage); } else { // SNI error. Replace the entire message. - // errorMessage = SQL.GetSNIErrorMessage((int)details.sniErrorNumber); // If its a LocalDB error, then nativeError actually contains a LocalDB-specific error code, not a win32 error code @@ -1536,6 +1533,7 @@ internal SqlError ProcessSNIError(TdsParserStateObject stateObj) errorMessage += LocalDBAPI.GetLocalDBMessage((int)details.nativeError); win32ErrorCode = 0; } + SqlClientEventSource.Log.TryAdvancedTraceEvent(" Extracting the latest exception from native SNI. errorMessage: {0}", errorMessage); } } errorMessage = string.Format("{0} (provider: {1}, error: {2} - {3})", @@ -12606,7 +12604,7 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar return true; // No data } - Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),"Out of sync plp read request"); + Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); charsLeft = len; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 9cb15d312d..84b7cc8e5d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -361,8 +361,12 @@ public static bool IsTargetReadyForAeWithKeyStore() public static bool IsNotUsingManagedSNIOnWindows() => !UseManagedSNIOnWindows; - public static bool IsUsingNativeSNI() => !IsUsingManagedSNI(); - + public static bool IsUsingNativeSNI() => +#if !NETFRAMEWORK + DataTestUtility.IsNotUsingManagedSNIOnWindows(); +#else + true; +#endif // Synapse: UTF8 collations are not supported with Azure Synapse. // Ref: https://feedback.azure.com/forums/307516-azure-synapse-analytics/suggestions/40103791-utf-8-collations-should-be-supported-in-azure-syna public static bool IsUTF8Supported() @@ -885,7 +889,7 @@ public static bool ParseDataSource(string dataSource, out string hostname, out i if (dataSource.Contains(",")) { - if (!Int32.TryParse(dataSource.Substring(dataSource.LastIndexOf(",",StringComparison.Ordinal) + 1), out port)) + if (!Int32.TryParse(dataSource.Substring(dataSource.LastIndexOf(",", StringComparison.Ordinal) + 1), out port)) { return false; } 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 0c06e6cf47..9b8284dcac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs @@ -16,6 +16,7 @@ private enum InfoType state } private static bool IsLocalDBEnvironmentSet() => DataTestUtility.IsLocalDBInstalled(); + private static bool IsNativeSNI() => DataTestUtility.IsUsingNativeSNI(); private static bool IsLocalDbSharedInstanceSet() => DataTestUtility.IsLocalDbSharedInstanceSetup(); private static readonly string s_localDbConnectionString = @$"server=(localdb)\{DataTestUtility.LocalDbAppName}"; private static readonly string[] s_sharedLocalDbInstances = new string[] { @$"server=(localdb)\.\{DataTestUtility.LocalDbSharedInstanceName}", @$"server=(localdb)\." }; @@ -123,6 +124,47 @@ public static void LocalDBNamepipeMarsTest() #endregion + #region Failures + // ToDo: After adding shared memory support on managed SNI, the IsNativeSNI could be taken out + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ConditionalTheory(nameof(IsLocalDBEnvironmentSet), nameof(IsNativeSNI))] + [InlineData("lpc:")] + public static void SharedMemoryAndSqlLocalDbConnectionTest(string prefix) + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = prefix + stringBuilder.DataSource; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + 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); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [InlineData("tcp:")] + [InlineData("np:")] + [InlineData("undefinded:")] + [ConditionalTheory(nameof(IsLocalDBEnvironmentSet)/*, nameof(IsNativeSNI)*/)] + public static void PrefixAndSqlLocalDbConnectionTest(string prefix) + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = prefix + stringBuilder.DataSource; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + 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); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.Uap)] // No Registry support on UAP + [ConditionalFact(nameof(IsLocalDBEnvironmentSet)/*, nameof(IsNativeSNI)*/)] + public static void InvalidSqlLocalDbConnectionTest() + { + SqlConnectionStringBuilder stringBuilder = new(s_localDbConnectionString); + stringBuilder.DataSource = stringBuilder.DataSource + "Invalid123"; + SqlException ex = Assert.Throws(() => ConnectionTest(stringBuilder.ConnectionString)); + 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); + if (IsNativeSNI()) + { + Assert.Contains("The specified LocalDB instance does not exist.", ex.Message); + } + } + #endregion + private static void ConnectionWithMarsTest(string connectionString) { SqlConnectionStringBuilder builder = new(connectionString) @@ -178,13 +220,13 @@ private static void RestartLocalDB() { string state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_sqlLocalDbInfo, InfoType.state); int count = 5; - while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count>0) + while (state.Equals("stopped", StringComparison.InvariantCultureIgnoreCase) && count > 0) { count--; state = ExecuteLocalDBCommandProcess(s_commandPrompt, s_startLocalDbCommand, InfoType.state); Thread.Sleep(2000); } - if(state == null || state != "Running") + if (state == null || state != "Running") { throw new LocalDBNotStartedException(); } From a8f79df8595f508ba4360fc9a4066af454cd666b Mon Sep 17 00:00:00 2001 From: Davoud Date: Mon, 28 Aug 2023 12:45:09 -0700 Subject: [PATCH 2/2] Fix format exception --- .../netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs index 152f016061..a81fb89dfb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/LocalDB.Windows.cs @@ -54,7 +54,7 @@ private string GetConnectionString(string localDbInstance) if (result != TdsEnums.SNI_SUCCESS) { SNILoadHandle.SingletonInstance.LastError = new SNIError(SNIProviders.INVALID_PROV, 0, SNICommon.LocalDBErrorCode, Strings.SNI_ERROR_50); - SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Unsuccessful 'LocalDBStartInstance' method call with {0} result to start '{1}' localDb instance", result, localDbInstance); + SqlClientEventSource.Log.TrySNITraceEvent(nameof(LocalDB), EventType.ERR, "Unsuccessful 'LocalDBStartInstance' method call with {0} result to start '{1}' localDb instance", args0: result, args1: localDbInstance); localDBConnectionString = null; } return localDBConnectionString?.ToString();