From 29048ab39c30d3fc6c24d1378b938e98626d7a0e Mon Sep 17 00:00:00 2001 From: kkurni Date: Thu, 17 Mar 2016 13:41:41 -0700 Subject: [PATCH] Fix SqlParameter Validation for Enum Type. This logic is the same as .net 4.5 but it use Convert.GetTypeCode instead of Type.GetTypeCode. --- .../System.Data.SqlClient.sln | 43 +-- .../src/System/Data/SqlClient/SqlEnums.cs | 274 +++++++++--------- .../ADO/ParametersTest/ParametersTest.cs | 73 ++++- .../ManualTests/DataCommon/DataTestClass.cs | 18 +- .../tests/ManualTests/README.md | 3 + 5 files changed, 249 insertions(+), 162 deletions(-) diff --git a/src/System.Data.SqlClient/System.Data.SqlClient.sln b/src/System.Data.SqlClient/System.Data.SqlClient.sln index 5360efb664ec..cac28999f358 100644 --- a/src/System.Data.SqlClient/System.Data.SqlClient.sln +++ b/src/System.Data.SqlClient/System.Data.SqlClient.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SqlClient", "src\System.Data.SqlClient.csproj", "{D4550556-4745-457F-BA8F-3EBF3836D6B4}" EndProject @@ -15,36 +14,44 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - Windows_Debug|Any CPU = Windows_Debug|Any CPU - Windows_Release|Any CPU = Windows_Release|Any CPU Unix_Debug|Any CPU = Unix_Debug|Any CPU Unix_Release|Any CPU = Unix_Release|Any CPU + Windows_Debug|Any CPU = Windows_Debug|Any CPU + Windows_Release|Any CPU = Windows_Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Debug|Any CPU.Build.0 = Windows_Debug|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.ActiveCfg = Windows_Release|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.Build.0 = Windows_Release|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.ActiveCfg = Unix_Release|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.Build.0 = Unix_Release|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Unix_Debug|Any CPU.ActiveCfg = Unix_Debug|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Unix_Release|Any CPU.ActiveCfg = Unix_Release|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Windows_Debug|Any CPU.ActiveCfg = Unix_Release|Any CPU + {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Windows_Release|Any CPU.ActiveCfg = Unix_Release|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Debug|Any CPU.Build.0 = Windows_Debug|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.ActiveCfg = Windows_Release|Any CPU {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.Build.0 = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Unix_Debug|Any CPU.ActiveCfg = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Unix_Debug|Any CPU.Build.0 = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Unix_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Unix_Release|Any CPU.Build.0 = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {F3E72F35-0351-4D67-9388-725BCAD807BA}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.Build.0 = Windows_Debug|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.ActiveCfg = Windows_Release|Any CPU {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.Build.0 = Windows_Release|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Debug|Any CPU.ActiveCfg = Unix_Debug|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Debug|Any CPU.Build.0 = Unix_Debug|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.ActiveCfg = Unix_Release|Any CPU - {D4550556-4745-457F-BA8F-3EBF3836D6B4}.Release|Any CPU.Build.0 = Unix_Release|Any CPU - {F3E72F35-0351-4D67-9388-725BCAD807BA}.Debug|Any CPU.ActiveCfg = Unix_Debug|Any CPU - {F3E72F35-0351-4D67-9388-725BCAD807BA}.Debug|Any CPU.Build.0 = Unix_Debug|Any CPU - {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.ActiveCfg = Unix_Release|Any CPU - {F3E72F35-0351-4D67-9388-725BCAD807BA}.Release|Any CPU.Build.0 = Unix_Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.ActiveCfg = Unix_Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Debug|Any CPU.Build.0 = Unix_Debug|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.ActiveCfg = Unix_Release|Any CPU - {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Release|Any CPU.Build.0 = Unix_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Unix_Debug|Any CPU.ActiveCfg = Windows_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Unix_Debug|Any CPU.Build.0 = Windows_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Unix_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Unix_Release|Any CPU.Build.0 = Windows_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU + {45DB5F86-7AE3-45C6-870D-F9357B66BDB5}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs index 022a1ff113e3..32de7930f6f9 100644 --- a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs +++ b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlEnums.cs @@ -378,150 +378,144 @@ static internal MetaType GetMetaTypeFromValue(object value, bool inferLen = true { throw ADP.InvalidDataType("null"); } - else if (value is DBNull) - { - throw ADP.InvalidDataType(typeof(DBNull).Name); - } - else if (value is bool) - { - return s_metaBit; - } - else if (value is char) - { - throw ADP.InvalidDataType(typeof(char).Name); - } - else if (value is sbyte) - { - throw ADP.InvalidDataType(typeof(sbyte).Name); - } - else if (value is byte) - { - return s_metaTinyInt; - } - else if (value is short) - { - return s_metaSmallInt; - } - else if (value is ushort) - { - throw ADP.InvalidDataType(typeof(ushort).Name); - } - else if (value is int) - { - return s_metaInt; - } - else if (value is uint) - { - throw ADP.InvalidDataType(typeof(uint).Name); - } - else if (value is long) - { - return s_metaBigInt; - } - else if (value is ulong) - { - throw ADP.InvalidDataType(typeof(ulong).Name); - } - else if (value is float) - { - return s_metaReal; - } - else if (value is double) - { - return s_metaFloat; - } - else if (value is decimal) - { - return MetaDecimal; - } - else if (value is DateTime) - { - return s_metaDateTime; - } - else if (value is string) - { - return (inferLen ? PromoteStringType((string)value) : MetaNVarChar); - } - else if (value is byte[]) - { - if (!inferLen || ((byte[])value).Length <= TdsEnums.TYPE_SIZE_LIMIT) - { - return MetaVarBinary; - } - else - { - return MetaImage; - } - } - else if (value is Guid) - { - return s_metaUniqueId; - } - else if (value is SqlBinary) - return MetaVarBinary; - else if (value is SqlBoolean) - return s_metaBit; - else if (value is SqlByte) - return s_metaTinyInt; - else if (value is SqlBytes) - return MetaVarBinary; - else if (value is SqlChars) - return MetaNVarChar; - else if (value is SqlDateTime) - return s_metaDateTime; - else if (value is SqlDouble) - return s_metaFloat; - else if (value is SqlGuid) - return s_metaUniqueId; - else if (value is SqlInt16) - return s_metaSmallInt; - else if (value is SqlInt32) - return s_metaInt; - else if (value is SqlInt64) - return s_metaBigInt; - else if (value is SqlMoney) - return s_metaMoney; - else if (value is SqlDecimal) - return MetaDecimal; - else if (value is SqlSingle) - return s_metaReal; - else if (value is SqlXml) - return MetaXml; - else if (value is SqlString) - { - return ((inferLen && !((SqlString)value).IsNull) ? PromoteStringType(((SqlString)value).Value) : MetaNVarChar); - } - else if (value is IEnumerable) - { - return s_metaTable; - } - else if (value is TimeSpan) - { - return MetaTime; - } - else if (value is DateTimeOffset) + + if (value is DBNull) { - return MetaDateTimeOffset; + throw ADP.InvalidDataType(nameof(DBNull)); } - else if (streamAllowed) + + Type dataType = value.GetType(); + switch (Convert.GetTypeCode(value)) { - // Derived from Stream ? - if (value is Stream) - { - return MetaVarBinary; - } - // Derived from TextReader ? - if (value is TextReader) - { - return MetaNVarChar; - } - // Derived from XmlReader ? - if (value is XmlReader) - { - return MetaXml; - } + case TypeCode.Empty: + throw ADP.InvalidDataType(nameof(TypeCode.Empty)); + case TypeCode.Object: + + if (dataType == typeof (System.Byte[])) + { + if (!inferLen || ((byte[]) value).Length <= TdsEnums.TYPE_SIZE_LIMIT) + { + return MetaVarBinary; + } + else + { + return MetaImage; + } + } + if (dataType == typeof (System.Guid)) + { + return s_metaUniqueId; + } + if (dataType == typeof (System.Object)) + { + return s_metaVariant; + } + // check sql types now + if (dataType == typeof (SqlBinary)) + return MetaVarBinary; + if (dataType == typeof (SqlBoolean)) + return s_metaBit; + if (dataType == typeof (SqlByte)) + return s_metaTinyInt; + if (dataType == typeof (SqlBytes)) + return MetaVarBinary; + if (dataType == typeof (SqlChars)) + return MetaNVarChar; + if (dataType == typeof (SqlDateTime)) + return s_metaDateTime; + if (dataType == typeof (SqlDouble)) + return s_metaFloat; + if (dataType == typeof (SqlGuid)) + return s_metaUniqueId; + if (dataType == typeof (SqlInt16)) + return s_metaSmallInt; + if (dataType == typeof (SqlInt32)) + return s_metaInt; + if (dataType == typeof (SqlInt64)) + return s_metaBigInt; + if (dataType == typeof (SqlMoney)) + return s_metaMoney; + if (dataType == typeof (SqlDecimal)) + return MetaDecimal; + if (dataType == typeof (SqlSingle)) + return s_metaReal; + if (dataType == typeof (SqlXml)) + return MetaXml; + if (dataType == typeof (SqlString)) + { + return ((inferLen && !((SqlString) value).IsNull) + ? PromoteStringType(((SqlString) value).Value) + : MetaNVarChar); + } + + if (dataType == typeof (IEnumerable) || dataType == typeof (DataTable)) + { + return s_metaTable; + } + + if (dataType == typeof (TimeSpan)) + { + return MetaTime; + } + + if (dataType == typeof (DateTimeOffset)) + { + return MetaDateTimeOffset; + } + + if (streamAllowed) + { + // Derived from Stream ? + if (value is Stream) + { + return MetaVarBinary; + } + // Derived from TextReader ? + if (value is TextReader) + { + return MetaNVarChar; + } + // Derived from XmlReader ? + if (value is XmlReader) + { + return MetaXml; + } + } + + throw ADP.UnknownDataType(dataType); + case TypeCode.Boolean: + return s_metaBit; + case TypeCode.Char: + throw ADP.InvalidDataType(nameof(TypeCode.Char)); + case TypeCode.SByte: + throw ADP.InvalidDataType(nameof(TypeCode.SByte)); + case TypeCode.Byte: + return s_metaTinyInt; + case TypeCode.Int16: + return s_metaSmallInt; + case TypeCode.UInt16: + throw ADP.InvalidDataType(nameof(TypeCode.UInt16)); + case TypeCode.Int32: + return s_metaInt; + case TypeCode.UInt32: + throw ADP.InvalidDataType(nameof(TypeCode.UInt32)); + case TypeCode.Int64: + return s_metaBigInt; + case TypeCode.UInt64: + throw ADP.InvalidDataType(nameof(TypeCode.UInt64)); + case TypeCode.Single: + return s_metaReal; + case TypeCode.Double: + return s_metaFloat; + case TypeCode.Decimal: + return MetaDecimal; + case TypeCode.DateTime: + return s_metaDateTime; + case TypeCode.String: + return (inferLen ? PromoteStringType((string) value) : MetaNVarChar); + default: + throw ADP.UnknownDataType(dataType); } - throw ADP.UnknownDataType(value.GetType()); } internal static object GetNullSqlValue(Type sqlType) diff --git a/src/System.Data.SqlClient/tests/ManualTests/ADO/ParametersTest/ParametersTest.cs b/src/System.Data.SqlClient/tests/ManualTests/ADO/ParametersTest/ParametersTest.cs index dc1cf2a5c8ee..c09d45d867fb 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/ADO/ParametersTest/ParametersTest.cs +++ b/src/System.Data.SqlClient/tests/ManualTests/ADO/ParametersTest/ParametersTest.cs @@ -3,12 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Data.SqlTypes; using Xunit; namespace System.Data.SqlClient.ManualTesting.Tests { - public class OleDbParametersTest + public class ParametersTest { + private static string _connString = DataTestClass.SQL2008_Northwind; + [Fact] public static void CodeCoverageSqlClient() { @@ -92,5 +95,73 @@ public static void CodeCoverageSqlClient() DataTestClass.AssertThrowsWrapper(() => new SqlCommand().Parameters.Remove(new SqlParameter()), "Attempted to remove an SqlParameter that is not contained by this SqlParameterCollection."); } + + [Fact] + public void Test_WithEnumValue_ShouldInferToUnderlyingType() + { + using (var conn = new SqlConnection(_connString)) + { + conn.Open(); + var cmd = new SqlCommand("select @input", conn); + cmd.Parameters.AddWithValue("@input", MyEnum.B); + object value = cmd.ExecuteScalar(); + Assert.Equal((MyEnum)value, MyEnum.B); + } + } + + [Fact] + public void Test_WithOutputEnumParameter_ShouldReturnEnum() + { + using (var conn = new SqlConnection(_connString)) + { + conn.Open(); + var cmd = new SqlCommand("set @output = @input", conn); + cmd.Parameters.AddWithValue("@input", MyEnum.B); + + var outputParam = cmd.CreateParameter(); + outputParam.ParameterName = "@output"; + outputParam.DbType = DbType.Int32; + outputParam.Direction = ParameterDirection.Output; + cmd.Parameters.Add(outputParam); + + cmd.ExecuteNonQuery(); + + Assert.Equal((MyEnum)outputParam.Value, MyEnum.B); + } + } + + [Fact] + public void Test_WithDecimalValue_ShouldReturnDecimal() + { + using (var conn = new SqlConnection(_connString)) + { + conn.Open(); + var cmd = new SqlCommand("select @foo", conn); + cmd.Parameters.AddWithValue("@foo", new SqlDecimal(0.5)); + var result = (decimal)cmd.ExecuteScalar(); + Assert.Equal(result, (decimal)0.5); + } + } + + [Fact] + public void Test_WithGuidValue_ShouldReturnGuid() + { + using (var conn = new SqlConnection(_connString)) + { + conn.Open(); + var expectedGuid = Guid.NewGuid(); + var cmd = new SqlCommand("select @input", conn); + cmd.Parameters.AddWithValue("@input", expectedGuid); + var result = cmd.ExecuteScalar(); + Assert.Equal(expectedGuid, (Guid)result); + } + } + + enum MyEnum + { + A = 1, + B = 2 + } + } } diff --git a/src/System.Data.SqlClient/tests/ManualTests/DataCommon/DataTestClass.cs b/src/System.Data.SqlClient/tests/ManualTests/DataCommon/DataTestClass.cs index feeb3de272b3..8a4ee8b32a2f 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/DataCommon/DataTestClass.cs +++ b/src/System.Data.SqlClient/tests/ManualTests/DataCommon/DataTestClass.cs @@ -75,8 +75,16 @@ private static string GetConnectionStringFromXml(string key) private static void PopulateConnectionStrings() { s_xmlConnectionStringMap = new Dictionary(); - - TextReader connectionStringReader = File.OpenText(@"ConnectionString.xml"); + TextReader connectionStringReader = null; + //This override connection string is used for security reason. so that you can add this your local binary folder. Modifying ConnectionString.xml will caused sync into source file. + if (File.Exists("ConnectionString.Override.xml")) + { + connectionStringReader = File.OpenText(@"ConnectionString.override.xml"); + } + else + { + connectionStringReader = File.OpenText(@"ConnectionString.xml"); + } XmlReaderSettings settings = new XmlReaderSettings(); settings.IgnoreWhitespace = true; settings.DtdProcessing = DtdProcessing.Ignore; @@ -94,7 +102,11 @@ private static void PopulateConnectionStrings() { string connectionStringKey = node.Attributes["id"].Value; XmlText connectionStringXml = node.FirstChild as XmlText; - string connectionStringValue = connectionStringXml.Data; + string connectionStringValue = string.Empty; + if (connectionStringXml != null) + { + connectionStringValue = connectionStringXml.Data; + } s_xmlConnectionStringMap[connectionStringKey] = connectionStringValue; } } diff --git a/src/System.Data.SqlClient/tests/ManualTests/README.md b/src/System.Data.SqlClient/tests/ManualTests/README.md index 0bb689d95abf..1a842dda0e69 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/README.md +++ b/src/System.Data.SqlClient/tests/ManualTests/README.md @@ -6,3 +6,6 @@ Note: These tests are disabled by default so that they aren't run automatically. Instructions for running tests: [Unix](https://github.com/dotnet/corefx/blob/master/Documentation/building/cross-platform-testing.md) and [Windows](https://github.com/dotnet/corefx/blob/master/Documentation/building/windows-instructions.md). Documentation for connection string parameters: [SqlConnection.ConnectionString](https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring.aspx). + +Tips: Modifying ConnectionString.xml inside bin/test/... will sync into the source as well +To avoid accidently pushing ConnectionString.xml to github. You can put ConnectionString.Override.xml in your bin/test/.. folder.