diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs index 6825139a15..72051fec39 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlDataRecord.cs @@ -477,6 +477,12 @@ internal void ThrowIfInvalidOrdinal(int ordinal) } } + internal SmiMetaData GetVariantInternalMetaData(int ordinal) + { + ThrowIfInvalidOrdinal(ordinal); + return _recordBuffer.GetVariantType(ordinal); + } + /// [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] IDataReader System.Data.IDataRecord.GetData(int ordinal) => throw ADP.NotSupported(); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlRecordBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlRecordBuffer.cs index d8e9dc94c6..bb9bdbc446 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlRecordBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/SqlRecordBuffer.cs @@ -463,7 +463,7 @@ internal SmiMetaData VariantType } set { - Debug.Assert(value != null && (value.SqlDbType == SqlDbType.Money || value.SqlDbType == SqlDbType.NVarChar), + Debug.Assert(value != null && (value.SqlDbType is SqlDbType.Money or SqlDbType.NVarChar or SqlDbType.Date), "Invalid metadata"); _metadata = value; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs index 94338c8dfb..1fc31c287a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.cs @@ -1504,7 +1504,7 @@ value is DataFeed } #if NET case ExtendedClrTypeCode.DateOnly: - SetDateTime_Checked(setters, ordinal, metaData, ((DateOnly)value).ToDateTime(new TimeOnly(0, 0))); + SetDate_Checked(setters, ordinal, metaData, ((DateOnly)value).ToDateTime(new TimeOnly(0, 0))); break; case ExtendedClrTypeCode.TimeOnly: SetTimeSpan_Checked(setters, ordinal, metaData, ((TimeOnly)value).ToTimeSpan()); @@ -2033,9 +2033,35 @@ internal static void FillCompatibleSettersFromRecord(SmiTypedGetterSetter setter SetSqlXml_Unchecked(setters, i, record.GetSqlXml(i)); // perf improvement? break; case SqlDbType.Variant: - object o = record.GetSqlValue(i); + SmiMetaData variantMetadata = record.GetVariantInternalMetaData(i); + SqlBuffer.StorageType storageType; + object o; + + // We cannot transport a DateOnly instance using the GetSqlValue method. This is because + // GetSqlValue will return a SqlDateTime, which has a much tighter range of expected + // values (so trying to send DateOnly.MinValue will overflow.) Instead, use GetValue (which + // will return a DateTime, with identical ranges of expected values.) + if (variantMetadata.SqlDbType is SqlDbType.Date) + { + storageType = SqlBuffer.StorageType.Date; + o = record.GetValue(i); + } + else + { + storageType = SqlBuffer.StorageType.Empty; + o = record.GetSqlValue(i); + } + ExtendedClrTypeCode typeCode = MetaDataUtilsSmi.DetermineExtendedTypeCode(o); - SetCompatibleValueV200(setters, i, metaData[i], o, typeCode, 0, null /* no peekahead */); + + if (storageType is not SqlBuffer.StorageType.Empty) + { + SetCompatibleValueV200(setters, i, metaData[i], o, typeCode, 0, null /* no peekahead */, storageType); + } + else + { + SetCompatibleValueV200(setters, i, metaData[i], o, typeCode, 0, null /* no peekahead */); + } break; case SqlDbType.Udt: Debug.Assert(CanAccessSetterDirectly(metaData[i], ExtendedClrTypeCode.SqlBytes)); @@ -3135,8 +3161,11 @@ private static void SetDateTime2_Unchecked(ITypedSettersV3 setters, int ordinal, private static void SetDate_Unchecked(ITypedSettersV3 setters, int ordinal, SmiMetaData metaData, DateTime value) { - Debug.Assert(metaData.SqlDbType == SqlDbType.Variant, "Invalid type. This should be called only when the type is variant."); - setters.SetVariantMetaData(ordinal, SmiMetaData.DefaultDate); + if (metaData.SqlDbType == SqlDbType.Variant) + { + setters.SetVariantMetaData(ordinal, SmiMetaData.DefaultDate); + } + setters.SetDateTime(ordinal, value); } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTests.cs index 3d84e0ff2c..e968295b5a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/DateTimeVariantTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -987,12 +987,7 @@ public static IEnumerable GetParameterCombinations() { #if NET yield return new object[] { DateOnly.MinValue, "date", - new Dictionary { - { TestVariations.TestSimpleParameter_Variant, SqlDateTimeOverflow }, - { TestVariations.TestSqlDataRecordParameterToTVP_Variant, SqlDateTimeOverflow }, - { TestVariations.TestSqlDataReaderParameterToTVP_Variant, SqlDateTimeOverflow }, - { TestVariations.SqlBulkCopyDataTable_Variant, SqlDateTimeOverflow }, - { TestVariations.SqlBulkCopyDataRow_Variant, SqlDateTimeOverflow }}, + new Dictionary(), new Dictionary() { { TestVariations.TestSimpleParameter_Type, new DateTime(0) }, @@ -1014,12 +1009,7 @@ public static IEnumerable GetParameterCombinations() }, new Dictionary()}; yield return new object[] { DateOnly.MaxValue, "date", - new Dictionary { - { TestVariations.TestSimpleParameter_Variant, SqlDateTimeOverflow }, - { TestVariations.TestSqlDataRecordParameterToTVP_Variant, SqlDateTimeOverflow }, - { TestVariations.TestSqlDataReaderParameterToTVP_Variant, SqlDateTimeOverflow }, - { TestVariations.SqlBulkCopyDataTable_Variant, SqlDateTimeOverflow }, - { TestVariations.SqlBulkCopyDataRow_Variant, SqlDateTimeOverflow }}, + new Dictionary(), new Dictionary() { { TestVariations.TestSimpleParameter_Type, new DateTime(3155378112000000000) }, @@ -1040,9 +1030,6 @@ public static IEnumerable GetParameterCombinations() { TestVariations.SqlBulkCopyDataRow_Variant, new DateTime(3155378112000000000) } }, new Dictionary() - { - {TestVariations.TestSqlDataRecordParameterToTVP_Variant, "datetime"} - } }; #endif yield return new object[] { DateTime.MinValue, "date",