1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 using System.IO; 7 using System.Runtime.InteropServices; 8 using System.Xml; 9 10 namespace System.Data.SqlTypes 11 { 12 /// <summary> 13 /// This type provides workarounds for the separation between System.Data.Common 14 /// and System.Data.SqlClient. The latter wants to access internal members of the former, and 15 /// this class provides ways to do that. We must review and update this implementation any time the 16 /// implementation of the corresponding types in System.Data.Common change. 17 /// </summary> 18 internal static class SqlTypeWorkarounds 19 { 20 #region Work around inability to access SqlXml.CreateSqlXmlReader 21 private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment }; 22 private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; 23 private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new XmlReaderSettings() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true }; 24 25 internal const SqlCompareOptions SqlStringValidSqlCompareOptionMask = 26 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | 27 SqlCompareOptions.IgnoreNonSpace | SqlCompareOptions.IgnoreKanaType | 28 SqlCompareOptions.BinarySort | SqlCompareOptions.BinarySort2; 29 SqlXmlCreateSqlXmlReader(Stream stream, bool closeInput = false, bool async = false)30 internal static XmlReader SqlXmlCreateSqlXmlReader(Stream stream, bool closeInput = false, bool async = false) 31 { 32 Debug.Assert(closeInput || !async, "Currently we do not have pre-created settings for !closeInput+async"); 33 34 XmlReaderSettings settingsToUse = closeInput ? 35 (async ? s_defaultXmlReaderSettingsAsyncCloseInput : s_defaultXmlReaderSettingsCloseInput) : 36 s_defaultXmlReaderSettings; 37 38 return XmlReader.Create(stream, settingsToUse); 39 } 40 #endregion 41 42 #region Work around inability to access SqlDateTime.ToDateTime SqlDateTimeToDateTime(int daypart, int timepart)43 internal static DateTime SqlDateTimeToDateTime(int daypart, int timepart) 44 { 45 // Values need to match those from SqlDateTime 46 const double SQLTicksPerMillisecond = 0.3; 47 const int SQLTicksPerSecond = 300; 48 const int SQLTicksPerMinute = SQLTicksPerSecond * 60; 49 const int SQLTicksPerHour = SQLTicksPerMinute * 60; 50 const int SQLTicksPerDay = SQLTicksPerHour * 24; 51 const int MinDay = -53690; // Jan 1 1753 52 const int MaxDay = 2958463; // Dec 31 9999 is this many days from Jan 1 1900 53 const int MinTime = 0; // 00:00:0:000PM 54 const int MaxTime = SQLTicksPerDay - 1; // = 25919999, 11:59:59:997PM 55 56 if (daypart < MinDay || daypart > MaxDay || timepart < MinTime || timepart > MaxTime) 57 { 58 throw new OverflowException(SQLResource.DateTimeOverflowMessage); 59 } 60 61 long baseDateTicks = new DateTime(1900, 1, 1).Ticks; 62 long dayticks = daypart * TimeSpan.TicksPerDay; 63 long timeticks = ((long)(timepart / SQLTicksPerMillisecond + 0.5)) * TimeSpan.TicksPerMillisecond; 64 65 return new DateTime(baseDateTicks + dayticks + timeticks); 66 } 67 #endregion 68 69 #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation 70 /// <summary> 71 /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists 72 /// only to distinguish this constructor from the constructor that takes a long. 73 /// Used only internally. 74 /// </summary> SqlMoneyCtor(long value, int ignored)75 internal static SqlMoney SqlMoneyCtor(long value, int ignored) 76 { 77 var c = default(SqlMoneyCaster); 78 79 // Same behavior as the internal SqlMoney.ctor(long, bool) overload 80 c.Fake._fNotNull = true; 81 c.Fake._value = value; 82 83 return c.Real; 84 } 85 SqlMoneyToSqlInternalRepresentation(SqlMoney money)86 internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money) 87 { 88 var c = default(SqlMoneyCaster); 89 c.Real = money; 90 91 // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation 92 if (money.IsNull) 93 { 94 throw new SqlNullValueException(); 95 } 96 return c.Fake._value; 97 } 98 99 [StructLayout(LayoutKind.Sequential)] 100 private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields 101 { 102 internal bool _fNotNull; 103 internal long _value; 104 } 105 106 [StructLayout(LayoutKind.Explicit)] 107 private struct SqlMoneyCaster 108 { 109 [FieldOffset(0)] 110 internal SqlMoney Real; 111 [FieldOffset(0)] 112 internal SqlMoneyLookalike Fake; 113 } 114 #endregion 115 116 #region Work around inability to access SqlDecimal._data1/2/3/4 SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4)117 internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4) 118 { 119 // Extract the four data elements from SqlDecimal. 120 var c = default(SqlDecimalCaster); 121 c.Real = d; 122 data1 = c.Fake._data1; 123 data2 = c.Fake._data2; 124 data3 = c.Fake._data3; 125 data4 = c.Fake._data4; 126 } 127 128 [StructLayout(LayoutKind.Sequential)] 129 private struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields 130 { 131 internal byte _bStatus; 132 internal byte _bLen; 133 internal byte _bPrec; 134 internal byte _bScale; 135 internal uint _data1; 136 internal uint _data2; 137 internal uint _data3; 138 internal uint _data4; 139 } 140 141 [StructLayout(LayoutKind.Explicit)] 142 private struct SqlDecimalCaster 143 { 144 [FieldOffset(0)] 145 internal SqlDecimal Real; 146 [FieldOffset(0)] 147 internal SqlDecimalLookalike Fake; 148 } 149 #endregion 150 151 #region Work around inability to access SqlBinary.ctor(byte[], bool) SqlBinaryCtor(byte[] value, bool ignored)152 internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored) 153 { 154 // Construct a SqlBinary without allocating/copying the byte[]. This provides 155 // the same behavior as SqlBinary.ctor(byte[], bool). 156 var c = default(SqlBinaryCaster); 157 c.Fake._value = value; 158 return c.Real; 159 } 160 161 [StructLayout(LayoutKind.Sequential)] 162 private struct SqlBinaryLookalike 163 { 164 internal byte[] _value; 165 } 166 167 [StructLayout(LayoutKind.Explicit)] 168 private struct SqlBinaryCaster 169 { 170 [FieldOffset(0)] 171 internal SqlBinary Real; 172 [FieldOffset(0)] 173 internal SqlBinaryLookalike Fake; 174 } 175 #endregion 176 177 #region Work around inability to access SqlGuid.ctor(byte[], bool) SqlGuidCtor(byte[] value, bool ignored)178 internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) 179 { 180 // Construct a SqlGuid without allocating/copying the byte[]. This provides 181 // the same behavior as SqlGuid.ctor(byte[], bool). 182 var c = default(SqlGuidCaster); 183 c.Fake._value = value; 184 return c.Real; 185 } 186 187 [StructLayout(LayoutKind.Sequential)] 188 private struct SqlGuidLookalike 189 { 190 internal byte[] _value; 191 } 192 193 [StructLayout(LayoutKind.Explicit)] 194 private struct SqlGuidCaster 195 { 196 [FieldOffset(0)] 197 internal SqlGuid Real; 198 [FieldOffset(0)] 199 internal SqlGuidLookalike Fake; 200 } 201 #endregion 202 } 203 } 204