diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs index a4d197d5e2..bd9aa0cf20 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs @@ -314,7 +314,7 @@ private static ICollection SplitErrorNumberList(string list) HashSet set = new HashSet(); for (int index = 0; index < parts.Length; index++) { - if (int.TryParse(parts[index], System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowTrailingWhite, null, out int value)) + if (int.TryParse(parts[index], System.Globalization.NumberStyles.Integer, null, out int value)) { set.Add(value); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs index 7162c4dcaa..9c2f95f7a6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicConfigHelper.cs @@ -28,6 +28,9 @@ public class RetryLogicConfigHelper private const string SqlRetryLogicTypeName = "Microsoft.Data.SqlClient.SqlRetryLogic"; + private const string CreateExceptionMethodName = "CreateException"; + private const string AddMethodName = "Add"; + public const string DefaultTransientErrors = "1204, 1205, 1222, 49918, 49919, 49920, 4060, 4221, 40143, 40613, 40501, 40540, 40197, 42108, 42109, 10929, 10928, 10060, 10054, 10053, 997, 233, 64, 20, 0, -2, 207, 102, 2812"; private static readonly Random s_random = new Random(); @@ -36,13 +39,23 @@ public class RetryLogicConfigHelper private static readonly Type s_appContextSwitchManagerType = s_sqlClientAssembly.GetType(AppContextSwitchManagerTypeName); private static readonly Type s_sqlretrylogicType = s_sqlClientAssembly.GetType(SqlRetryLogicTypeName); private static readonly Type s_configurationLoaderType = s_sqlClientAssembly.GetType(ConfigurationLoaderTypeName); + private static readonly Type s_sqlErrorType = typeof(SqlError); + private static readonly Type s_sqlErrorCollectionType = typeof(SqlErrorCollection); private static readonly Type[] s_cfgLoaderParamsType = new Type[] { s_sqlClientAssembly.GetType(InterfaceCnnCfgTypeName), s_sqlClientAssembly.GetType(InterfaceCmdCfgTypeName), typeof(string), typeof(string) }; + private static readonly Type[] s_sqlErrorParamsType = new Type[] + { + typeof(int), typeof(byte), typeof(byte), + typeof(string), typeof(string), typeof(string), + typeof(int), typeof(Exception) + }; private static readonly ConstructorInfo s_loaderCtorInfo = s_configurationLoaderType.GetConstructor(s_cfgLoaderParamsType); + private static readonly ConstructorInfo s_sqlErrorCtorInfo = s_sqlErrorType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, s_sqlErrorParamsType, null); + private static readonly ConstructorInfo s_sqlErrorCollectionCtorInfo = s_sqlErrorCollectionType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); public static object CreateLoader(RetryLogicConfigs cnnConfig, RetryLogicConfigs cmdConfig) { @@ -164,6 +177,22 @@ public static RetryLogicConfigs CreateRandomConfig(string method, string authori }; } + public static SqlException CreateSqlException(int errorNumber) + { + MethodInfo addSqlErrorMethod = typeof(SqlErrorCollection).GetMethod(AddMethodName, BindingFlags.Instance | BindingFlags.NonPublic); + MethodInfo createExceptionMethod = typeof(SqlException).GetMethod(CreateExceptionMethodName, BindingFlags.Static | BindingFlags.NonPublic, + null, new Type[] { typeof(SqlErrorCollection), typeof(string) }, null); + + SqlError sqlError = s_sqlErrorCtorInfo.Invoke(new object[] { errorNumber, (byte)0, (byte)0, string.Empty, string.Empty, string.Empty, 0, null }) as SqlError; + SqlErrorCollection sqlErrorCollection = s_sqlErrorCollectionCtorInfo.Invoke(new object[0] { }) as SqlErrorCollection; + + addSqlErrorMethod.Invoke(sqlErrorCollection, new object[] { sqlError }); + + SqlException sqlException = createExceptionMethod.Invoke(null, new object[] { sqlErrorCollection, string.Empty }) as SqlException; + + return sqlException; + } + private static TimeSpan GenerateTimeSpan(TimeSpan start, TimeSpan end) { int max = (int)(end - start).TotalSeconds; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs index 67fd739f5f..e1590677dd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/SqlConfigurationManagerReliabilityTest.cs @@ -222,6 +222,30 @@ public void InvalidTransientError(string errors) } #endregion + #region Valid Configurations + [Theory] + [InlineData("-1,1,2,3")] + [InlineData("-1, 1, 2 , 3, -2")] + [InlineData("")] + public void ValidTransientError(string errors) + { + string[] transientErrorNumbers = errors.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + RetryLogicConfigs cnnCfg = RetryLogicConfigHelper.CreateRandomConfig(RetryLogicConfigHelper.RetryMethodName_Fix); + cnnCfg.TransientErrors = errors; + RetryLogicConfigs cmdCfg = RetryLogicConfigHelper.CreateRandomConfig(RetryLogicConfigHelper.RetryMethodName_Fix, @"Don't care!"); + + RetryLogicConfigHelper.ReturnLoaderAndProviders(cnnCfg, cmdCfg, out SqlRetryLogicBaseProvider cnnProvider, out _); + + foreach(string errorString in transientErrorNumbers) + { + int errorNumber = int.Parse(errorString.Trim()); + SqlException transientException = RetryLogicConfigHelper.CreateSqlException(errorNumber); + + Assert.True(cnnProvider.RetryLogic.TransientPredicate(transientException), $"Error {errorNumber} is not considered transient by the predicate."); + } + } + #endregion + #region private methods private void TestConnection(SqlRetryLogicBaseProvider provider, RetryLogicConfigs cnfig) {