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