1 //------------------------------------------------------------------------------
2 // <copyright file="SqlDataReader.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 // <owner current="true" primary="false">Microsoft</owner>
8 //------------------------------------------------------------------------------
9 
10 namespace System.Data.SqlClient {
11     using System.Threading;
12     using System.Diagnostics;
13     using System.Reflection;
14     using System;
15     using System.Data;
16     using System.IO;
17     using System.Collections;
18     using System.Collections.Specialized;
19     using System.Data.Sql;
20     using System.Data.SqlTypes;
21     using System.Data.Common;
22     using System.Data.ProviderBase;
23     using System.ComponentModel;
24     using System.Globalization;
25     using System.Xml;
26     using System.Runtime.InteropServices;
27 
28     internal sealed class SqlBuffer {
29 
30         internal enum StorageType {
31             Empty = 0,
32             Boolean,
33             Byte,
34             DateTime,
35             Decimal,
36             Double,
37             Int16,
38             Int32,
39             Int64,
40             Money,
41             Single,
42             String,
43             SqlBinary,
44             SqlCachedBuffer,
45             SqlGuid,
46             SqlXml,
47             Date,
48             DateTime2,
49             DateTimeOffset,
50             Time,
51         }
52 
53         internal struct DateTimeInfo {
54             // This is used to store DateTime
55             internal Int32 daypart;
56             internal Int32 timepart;
57         }
58 
59         internal struct NumericInfo {
60             // This is used to store Decimal data
61             internal Int32 data1;
62             internal Int32 data2;
63             internal Int32 data3;
64             internal Int32 data4;
65             internal Byte  precision;
66             internal Byte  scale;
67             internal Boolean positive;
68         }
69 
70         internal struct TimeInfo {
71             internal Int64 ticks;
72             internal byte scale;
73         }
74 
75         internal struct DateTime2Info {
76             internal Int32 date;
77             internal TimeInfo timeInfo;
78         }
79 
80         internal struct DateTimeOffsetInfo {
81             internal DateTime2Info dateTime2Info;
82             internal Int16 offset;
83         }
84 
85         [StructLayout(LayoutKind.Explicit)]
86         internal struct Storage {
87             [FieldOffset(0)] internal Boolean       _boolean;
88             [FieldOffset(0)] internal Byte          _byte;
89             [FieldOffset(0)] internal DateTimeInfo  _dateTimeInfo;
90             [FieldOffset(0)] internal Double        _double;
91             [FieldOffset(0)] internal NumericInfo   _numericInfo;
92             [FieldOffset(0)] internal Int16         _int16;
93             [FieldOffset(0)] internal Int32         _int32;
94             [FieldOffset(0)] internal Int64         _int64;     // also used to store Money, UtcDateTime, Date , and Time
95             [FieldOffset(0)] internal Single        _single;
96             [FieldOffset(0)] internal TimeInfo      _timeInfo;
97             [FieldOffset(0)] internal DateTime2Info _dateTime2Info;
98             [FieldOffset(0)] internal DateTimeOffsetInfo _dateTimeOffsetInfo;
99         }
100 
101         private bool         _isNull;
102         private StorageType  _type;
103         private Storage      _value;
104         private object       _object;    // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml
105 
SqlBuffer()106         internal SqlBuffer() {
107         }
108 
SqlBuffer(SqlBuffer value)109         private SqlBuffer(SqlBuffer value) { // Clone
110             // value types
111             _isNull    = value._isNull;
112             _type      = value._type;
113             // ref types - should also be read only unless at some point we allow this data
114             // to be mutable, then we will need to copy
115             _value     = value._value;
116             _object    = value._object;
117         }
118 
119         internal bool IsEmpty {
120             get {
121                 return (StorageType.Empty == _type);
122             }
123         }
124 
125         internal bool IsNull {
126             get {
127                 return _isNull;
128             }
129         }
130 
131         internal StorageType VariantInternalStorageType
132         {
133             get { return _type; }
134         }
135 
136         internal Boolean Boolean {
137             get {
138                 ThrowIfNull();
139 
140                 if (StorageType.Boolean == _type) {
141                     return _value._boolean;
142                 }
143                 return (Boolean)this.Value; // anything else we haven't thought of goes through boxing.
144             }
145             set {
146                 Debug.Assert (IsEmpty, "setting value a second time?");
147                 _value._boolean = value;
148                 _type = StorageType.Boolean;
149                 _isNull = false;
150             }
151         }
152 
153         internal Byte Byte {
154             get {
155                 ThrowIfNull();
156 
157                 if (StorageType.Byte == _type) {
158                     return _value._byte;
159                 }
160                 return (Byte)this.Value; // anything else we haven't thought of goes through boxing.
161             }
162             set {
163                 Debug.Assert (IsEmpty, "setting value a second time?");
164                 _value._byte = value;
165                 _type = StorageType.Byte;
166                 _isNull = false;
167             }
168         }
169 
170         internal Byte[] ByteArray {
171             get {
172                 ThrowIfNull();
173                 return this.SqlBinary.Value;    //
174             }
175         }
176 
177         internal DateTime DateTime {
178             get {
179                 ThrowIfNull();
180 
181                 if (StorageType.Date == _type) {
182                     return DateTime.MinValue.AddDays(_value._int32);
183                 }
184                 if (StorageType.DateTime2 == _type) {
185                     return new DateTime(GetTicksFromDateTime2Info(_value._dateTime2Info));
186                 }
187                 if (StorageType.DateTime == _type) {
188                     return SqlDateTime.ToDateTime(_value._dateTimeInfo.daypart, _value._dateTimeInfo.timepart);
189                 }
190                 return (DateTime)this.Value; // anything else we haven't thought of goes through boxing.
191             }
192         }
193 
194         internal Decimal Decimal {
195             get {
196                 ThrowIfNull();
197 
198                 if (StorageType.Decimal == _type) {
199                     if (_value._numericInfo.data4 != 0 || _value._numericInfo.scale > 28) {
200                         throw new OverflowException(SQLResource.ConversionOverflowMessage);
201                     }
202                     return new Decimal(_value._numericInfo.data1, _value._numericInfo.data2, _value._numericInfo.data3, !_value._numericInfo.positive, _value._numericInfo.scale);
203                 }
204                 if (StorageType.Money == _type) {
205                     long l = _value._int64;
206                     bool isNegative = false;
207                     if (l < 0) {
208                         isNegative = true;
209                         l = -l;
210                     }
211                     return new Decimal((int)(l & 0xffffffff), (int)(l >> 32), 0, isNegative, 4);
212                 }
213                 return (Decimal)this.Value; // anything else we haven't thought of goes through boxing.
214             }
215         }
216 
217         internal Double Double {
218             get {
219                 ThrowIfNull();
220 
221                 if (StorageType.Double == _type) {
222                     return _value._double;
223                 }
224                 return (Double)this.Value; // anything else we haven't thought of goes through boxing.
225             }
226             set {
227                 Debug.Assert (IsEmpty, "setting value a second time?");
228                 _value._double = value;
229                 _type = StorageType.Double;
230                 _isNull = false;
231             }
232         }
233 
234         internal Guid Guid {
235             get {
236                 //
237                 ThrowIfNull();
238                 return this.SqlGuid.Value;
239             }
240         }
241 
242         internal Int16 Int16 {
243             get {
244                 ThrowIfNull();
245 
246                 if (StorageType.Int16 == _type) {
247                     return _value._int16;
248                 }
249                 return (Int16)this.Value; // anything else we haven't thought of goes through boxing.
250             }
251             set {
252                 Debug.Assert (IsEmpty, "setting value a second time?");
253                 _value._int16 = value;
254                 _type = StorageType.Int16;
255                 _isNull = false;
256             }
257         }
258 
259         internal Int32 Int32 {
260             get {
261                 ThrowIfNull();
262 
263                 if (StorageType.Int32 == _type) {
264                     return _value._int32;
265                 }
266                 return (Int32)this.Value; // anything else we haven't thought of goes through boxing.
267             }
268             set {
269                 Debug.Assert (IsEmpty, "setting value a second time?");
270                 _value._int32 = value;
271                 _type = StorageType.Int32;
272                 _isNull = false;
273             }
274         }
275 
276         internal Int64 Int64 {
277             get {
278                 ThrowIfNull();
279 
280                 if (StorageType.Int64 == _type) {
281                     return _value._int64;
282                 }
283                 return (Int64)this.Value; // anything else we haven't thought of goes through boxing.
284             }
285             set {
286                 Debug.Assert (IsEmpty, "setting value a second time?");
287                 _value._int64 = value;
288                 _type = StorageType.Int64;
289                 _isNull = false;
290             }
291         }
292 
293         internal Single Single {
294             get {
295                 ThrowIfNull();
296 
297                 if (StorageType.Single == _type) {
298                     return _value._single;
299                 }
300                 return (Single)this.Value; // anything else we haven't thought of goes through boxing.
301             }
302             set {
303                 Debug.Assert (IsEmpty, "setting value a second time?");
304                 _value._single = value;
305                 _type = StorageType.Single;
306                 _isNull = false;
307             }
308         }
309 
310         internal String String {
311             get {
312                 ThrowIfNull();
313 
314                 if (StorageType.String == _type) {
315                     return (String)_object;
316                 }
317                 else if (StorageType.SqlCachedBuffer == _type) {
318                     return ((SqlCachedBuffer)(_object)).ToString();
319                 }
320                 return (String)this.Value; // anything else we haven't thought of goes through boxing.
321             }
322         }
323 
324         // use static list of format strings indexed by scale for perf!
325         private static string[] __katmaiDateTimeOffsetFormatByScale = new string[] {
326                 "yyyy-MM-dd HH:mm:ss zzz",
327                 "yyyy-MM-dd HH:mm:ss.f zzz",
328                 "yyyy-MM-dd HH:mm:ss.ff zzz",
329                 "yyyy-MM-dd HH:mm:ss.fff zzz",
330                 "yyyy-MM-dd HH:mm:ss.ffff zzz",
331                 "yyyy-MM-dd HH:mm:ss.fffff zzz",
332                 "yyyy-MM-dd HH:mm:ss.ffffff zzz",
333                 "yyyy-MM-dd HH:mm:ss.fffffff zzz",
334         };
335 
336         private static string[] __katmaiDateTime2FormatByScale = new string[] {
337                 "yyyy-MM-dd HH:mm:ss",
338                 "yyyy-MM-dd HH:mm:ss.f",
339                 "yyyy-MM-dd HH:mm:ss.ff",
340                 "yyyy-MM-dd HH:mm:ss.fff",
341                 "yyyy-MM-dd HH:mm:ss.ffff",
342                 "yyyy-MM-dd HH:mm:ss.fffff",
343                 "yyyy-MM-dd HH:mm:ss.ffffff",
344                 "yyyy-MM-dd HH:mm:ss.fffffff",
345         };
346 
347         private static string[] __katmaiTimeFormatByScale = new string[] {
348                 "HH:mm:ss",
349                 "HH:mm:ss.f",
350                 "HH:mm:ss.ff",
351                 "HH:mm:ss.fff",
352                 "HH:mm:ss.ffff",
353                 "HH:mm:ss.fffff",
354                 "HH:mm:ss.ffffff",
355                 "HH:mm:ss.fffffff",
356         };
357 
358         internal string KatmaiDateTimeString {
359             get {
360                 ThrowIfNull();
361 
362                 if (StorageType.Date == _type) {
363                     return this.DateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
364                 }
365                 if (StorageType.Time == _type) {
366                     byte scale = _value._timeInfo.scale;
367                     return new DateTime(_value._timeInfo.ticks).ToString(__katmaiTimeFormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
368                 }
369                 if (StorageType.DateTime2 == _type) {
370                     byte scale = _value._dateTime2Info.timeInfo.scale;
371                     return this.DateTime.ToString(__katmaiDateTime2FormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
372                 }
373                 if (StorageType.DateTimeOffset == _type) {
374                     DateTimeOffset dto = this.DateTimeOffset;
375                     byte scale = _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.scale;
376                     return dto.ToString(__katmaiDateTimeOffsetFormatByScale[scale], DateTimeFormatInfo.InvariantInfo);
377                 }
378                 return (String)this.Value; // anything else we haven't thought of goes through boxing.
379             }
380         }
381 
382         internal SqlString KatmaiDateTimeSqlString {
383             get {
384                 if (StorageType.Date == _type ||
385                     StorageType.Time == _type ||
386                     StorageType.DateTime2 == _type ||
387                     StorageType.DateTimeOffset == _type) {
388                     if (IsNull) {
389                         return SqlString.Null;
390                     }
391                     return new SqlString(KatmaiDateTimeString);
392                 }
393                 return (SqlString)this.SqlValue; // anything else we haven't thought of goes through boxing.
394             }
395         }
396 
397         internal TimeSpan Time {
398             get {
399                 ThrowIfNull();
400 
401                 if (StorageType.Time == _type) {
402                     return new TimeSpan(_value._timeInfo.ticks);
403                 }
404 
405                 return (TimeSpan)this.Value; // anything else we haven't thought of goes through boxing.
406             }
407         }
408 
409         internal DateTimeOffset DateTimeOffset {
410             get {
411                 ThrowIfNull();
412 
413                 if (StorageType.DateTimeOffset == _type) {
414                     TimeSpan offset = new TimeSpan(0, _value._dateTimeOffsetInfo.offset, 0);
415                     // datetime part presents time in UTC
416                     return new DateTimeOffset(GetTicksFromDateTime2Info(_value._dateTimeOffsetInfo.dateTime2Info) + offset.Ticks, offset);
417                 }
418 
419                 return (DateTimeOffset)this.Value; // anything else we haven't thought of goes through boxing.
420             }
421         }
422 
GetTicksFromDateTime2Info(DateTime2Info dateTime2Info)423         private static long GetTicksFromDateTime2Info(DateTime2Info dateTime2Info) {
424             return (dateTime2Info.date * TimeSpan.TicksPerDay + dateTime2Info.timeInfo.ticks);
425         }
426 
427         internal SqlBinary SqlBinary {
428             get {
429                 if (StorageType.SqlBinary == _type) {
430                     return (SqlBinary)_object;
431                 }
432                 return (SqlBinary)this.SqlValue; // anything else we haven't thought of goes through boxing.
433             }
434             set {
435                 Debug.Assert (IsEmpty, "setting value a second time?");
436                 _object = value;
437                 _type = StorageType.SqlBinary;
438                 _isNull = value.IsNull;
439             }
440         }
441 
442         internal SqlBoolean SqlBoolean {
443             get {
444                 if (StorageType.Boolean == _type) {
445                     if (IsNull) {
446                         return SqlBoolean.Null;
447                     }
448                     return new SqlBoolean(_value._boolean);
449                 }
450                 return (SqlBoolean)this.SqlValue; // anything else we haven't thought of goes through boxing.
451             }
452         }
453 
454         internal SqlByte SqlByte {
455             get {
456                 if (StorageType.Byte == _type) {
457                     if (IsNull) {
458                         return SqlByte.Null;
459                     }
460                     return new SqlByte(_value._byte);
461                 }
462                 return (SqlByte)this.SqlValue; // anything else we haven't thought of goes through boxing.
463             }
464         }
465 
466         internal SqlCachedBuffer SqlCachedBuffer {
467             get {
468                 if (StorageType.SqlCachedBuffer == _type) {
469                     if (IsNull) {
470                         return SqlCachedBuffer.Null;
471                     }
472                     return (SqlCachedBuffer)_object;
473                 }
474                 return (SqlCachedBuffer)this.SqlValue; // anything else we haven't thought of goes through boxing.
475             }
476             set {
477                 Debug.Assert (IsEmpty, "setting value a second time?");
478                 _object = value;
479                 _type = StorageType.SqlCachedBuffer;
480                 _isNull = value.IsNull;
481             }
482         }
483 
484         internal SqlXml SqlXml {
485             get {
486                 if (StorageType.SqlXml == _type) {
487                     if (IsNull) {
488                         return SqlXml.Null;
489                     }
490                     return (SqlXml)_object;
491                 }
492                 return (SqlXml)this.SqlValue; // anything else we haven't thought of goes through boxing.
493             }
494             set {
495                 Debug.Assert (IsEmpty, "setting value a second time?");
496                 _object = value;
497                 _type = StorageType.SqlXml;
498                 _isNull = value.IsNull;
499             }
500         }
501 
502         internal SqlDateTime SqlDateTime {
503             get {
504                 if (StorageType.DateTime == _type) {
505                     if (IsNull) {
506                         return SqlDateTime.Null;
507                     }
508                     return new SqlDateTime(_value._dateTimeInfo.daypart, _value._dateTimeInfo.timepart);
509                 }
510                 return (SqlDateTime)SqlValue; // anything else we haven't thought of goes through boxing.
511             }
512         }
513 
514         internal SqlDecimal SqlDecimal {
515             get {
516                 if (StorageType.Decimal == _type) {
517                     if (IsNull) {
518                         return SqlDecimal.Null;
519                     }
520                     return new SqlDecimal(_value._numericInfo.precision,
521                                           _value._numericInfo.scale,
522                                           _value._numericInfo.positive,
523                                           _value._numericInfo.data1,
524                                           _value._numericInfo.data2,
525                                           _value._numericInfo.data3,
526                                           _value._numericInfo.data4
527                                           );
528                 }
529                 return (SqlDecimal)this.SqlValue; // anything else we haven't thought of goes through boxing.
530             }
531         }
532 
533         internal SqlDouble SqlDouble {
534             get {
535                 if (StorageType.Double == _type) {
536                     if (IsNull) {
537                         return SqlDouble.Null;
538                     }
539                     return new SqlDouble(_value._double);
540                 }
541                 return (SqlDouble)this.SqlValue; // anything else we haven't thought of goes through boxing.
542             }
543         }
544 
545         internal SqlGuid SqlGuid {
546             get {
547                 if (StorageType.SqlGuid == _type) {
548                     return (SqlGuid)_object;
549                 }
550                 return (SqlGuid)this.SqlValue; // anything else we haven't thought of goes through boxing.
551             }
552             set {
553                 Debug.Assert (IsEmpty, "setting value a second time?");
554                 _object = value;
555                 _type = StorageType.SqlGuid;
556                 _isNull = value.IsNull;
557             }
558         }
559 
560         internal SqlInt16 SqlInt16 {
561             get {
562                 if (StorageType.Int16 == _type) {
563                     if (IsNull) {
564                         return SqlInt16.Null;
565                     }
566                     return new SqlInt16(_value._int16);
567                 }
568                 return (SqlInt16)this.SqlValue; // anything else we haven't thought of goes through boxing.
569             }
570         }
571 
572         internal SqlInt32 SqlInt32 {
573             get {
574                 if (StorageType.Int32 == _type) {
575                     if (IsNull) {
576                         return SqlInt32.Null;
577                     }
578                     return new SqlInt32(_value._int32);
579                 }
580                 return (SqlInt32)this.SqlValue; // anything else we haven't thought of goes through boxing.
581             }
582         }
583 
584         internal SqlInt64 SqlInt64 {
585             get {
586                 if (StorageType.Int64 == _type) {
587                     if (IsNull) {
588                         return SqlInt64.Null;
589                     }
590                     return new SqlInt64(_value._int64);
591                 }
592                 return (SqlInt64)this.SqlValue; // anything else we haven't thought of goes through boxing.
593             }
594         }
595 
596         internal SqlMoney SqlMoney {
597             get {
598                 if (StorageType.Money == _type) {
599                     if (IsNull) {
600                         return SqlMoney.Null;
601                     }
602                     return new SqlMoney(_value._int64, 1/*ignored*/);
603                 }
604                 return (SqlMoney)this.SqlValue; // anything else we haven't thought of goes through boxing.
605             }
606         }
607 
608         internal SqlSingle SqlSingle {
609             get {
610                 if (StorageType.Single == _type) {
611                     if (IsNull) {
612                         return SqlSingle.Null;
613                     }
614                     return new SqlSingle(_value._single);
615                 }
616                 return (SqlSingle)this.SqlValue; // anything else we haven't thought of goes through boxing.
617             }
618         }
619 
620         internal SqlString SqlString {
621             get {
622                 if (StorageType.String == _type) {
623                     if (IsNull) {
624                         return SqlString.Null;
625                     }
626                     return new SqlString((String)_object);
627 
628                 }
629                 else if (StorageType.SqlCachedBuffer == _type) {
630                     SqlCachedBuffer data = (SqlCachedBuffer)(_object);
631                     if (data.IsNull) {
632                         return SqlString.Null;
633                     }
634                     return data.ToSqlString();
635                 }
636                 return (SqlString)this.SqlValue; // anything else we haven't thought of goes through boxing.
637             }
638         }
639 
640         internal object SqlValue {
641             get {
642                 switch (_type) {
643                     case StorageType.Empty:           return DBNull.Value;
644                     case StorageType.Boolean:         return SqlBoolean;
645                     case StorageType.Byte:            return SqlByte;
646                     case StorageType.DateTime:        return SqlDateTime;
647                     case StorageType.Decimal:         return SqlDecimal;
648                     case StorageType.Double:          return SqlDouble;
649                     case StorageType.Int16:           return SqlInt16;
650                     case StorageType.Int32:           return SqlInt32;
651                     case StorageType.Int64:           return SqlInt64;
652                     case StorageType.Money:           return SqlMoney;
653                     case StorageType.Single:          return SqlSingle;
654                     case StorageType.String:          return SqlString;
655                     case StorageType.SqlCachedBuffer:
656                     {
657                         SqlCachedBuffer data = (SqlCachedBuffer)(_object);
658                         if (data.IsNull) {
659                             return SqlXml.Null;
660                         }
661                         return data.ToSqlXml();
662                     }
663 
664                     case StorageType.SqlBinary:
665                     case StorageType.SqlGuid:
666                         return _object;
667 
668                     case StorageType.SqlXml: {
669                         if (_isNull) {
670                             return SqlXml.Null;
671                         }
672                         Debug.Assert(null != _object);
673                         return (SqlXml) _object;
674                     }
675                     case StorageType.Date:
676                     case StorageType.DateTime2:
677                         if (_isNull) {
678                             return DBNull.Value;
679                         }
680                         return DateTime;
681                     case StorageType.DateTimeOffset:
682                         if (_isNull) {
683                             return DBNull.Value;
684                         }
685                         return DateTimeOffset;
686                     case StorageType.Time:
687                         if (_isNull) {
688                             return DBNull.Value;
689                         }
690                         return Time;
691                 }
692                 return null; // need to return the value as an object of some SQL type
693             }
694         }
695 
696         internal object Value {
697             get {
698                 if (IsNull) {
699                     return DBNull.Value;
700                 }
701                 switch (_type) {
702                     case StorageType.Empty:           return DBNull.Value;
703                     case StorageType.Boolean:         return Boolean;
704                     case StorageType.Byte:            return Byte;
705                     case StorageType.DateTime:        return DateTime;
706                     case StorageType.Decimal:         return Decimal;
707                     case StorageType.Double:          return Double;
708                     case StorageType.Int16:           return Int16;
709                     case StorageType.Int32:           return Int32;
710                     case StorageType.Int64:           return Int64;
711                     case StorageType.Money:           return Decimal;
712                     case StorageType.Single:          return Single;
713                     case StorageType.String:          return String;
714                     case StorageType.SqlBinary:       return ByteArray;
715                     case StorageType.SqlCachedBuffer:
716                     {
717                         // If we have a CachedBuffer, it's because it's an XMLTYPE column
718                         // and we have to return a string when they're asking for the CLS
719                         // value of the column.
720                         return ((SqlCachedBuffer)(_object)).ToString();
721                     }
722                     case StorageType.SqlGuid:         return Guid;
723                     case StorageType.SqlXml: {
724                         // XMLTYPE columns must be returned as string when asking for the CLS value
725                         SqlXml data = (SqlXml)_object;
726                         string s = data.Value;
727                         return s;
728                     }
729                     case StorageType.Date:            return DateTime;
730                     case StorageType.DateTime2:       return DateTime;
731                     case StorageType.DateTimeOffset:  return DateTimeOffset;
732                     case StorageType.Time:            return Time;
733                 }
734                 return null; // need to return the value as an object of some CLS type
735             }
736         }
737 
GetTypeFromStorageType(bool isSqlType)738         internal Type GetTypeFromStorageType (bool isSqlType) {
739             if (isSqlType) {
740                 switch (_type) {
741                     case SqlBuffer.StorageType.Empty:           return null;
742                     case SqlBuffer.StorageType.Boolean:         return typeof(SqlBoolean);
743                     case SqlBuffer.StorageType.Byte:            return typeof(SqlByte);
744                     case SqlBuffer.StorageType.DateTime:        return typeof(SqlDateTime);
745                     case SqlBuffer.StorageType.Decimal:         return typeof(SqlDecimal);
746                     case SqlBuffer.StorageType.Double:          return typeof(SqlDouble);
747                     case SqlBuffer.StorageType.Int16:           return typeof(SqlInt16);
748                     case SqlBuffer.StorageType.Int32:           return typeof(SqlInt32);
749                     case SqlBuffer.StorageType.Int64:           return typeof(SqlInt64);
750                     case SqlBuffer.StorageType.Money:           return typeof(SqlMoney);
751                     case SqlBuffer.StorageType.Single:          return typeof(SqlSingle);
752                     case SqlBuffer.StorageType.String:          return typeof(SqlString);
753                     case SqlBuffer.StorageType.SqlCachedBuffer: return typeof(SqlString);
754                     case SqlBuffer.StorageType.SqlBinary:       return typeof(object);
755                     case SqlBuffer.StorageType.SqlGuid:         return typeof(object);
756                     case SqlBuffer.StorageType.SqlXml:          return typeof(SqlXml);
757                 }
758             }
759             else { //Is CLR Type
760                 switch (_type) {
761                     case SqlBuffer.StorageType.Empty:           return null;
762                     case SqlBuffer.StorageType.Boolean:         return typeof(Boolean);
763                     case SqlBuffer.StorageType.Byte:            return typeof(Byte);
764                     case SqlBuffer.StorageType.DateTime:        return typeof(DateTime);
765                     case SqlBuffer.StorageType.Decimal:         return typeof(Decimal);
766                     case SqlBuffer.StorageType.Double:          return typeof(Double);
767                     case SqlBuffer.StorageType.Int16:           return typeof(Int16);
768                     case SqlBuffer.StorageType.Int32:           return typeof(Int32);
769                     case SqlBuffer.StorageType.Int64:           return typeof(Int64);
770                     case SqlBuffer.StorageType.Money:           return typeof(Decimal);
771                     case SqlBuffer.StorageType.Single:          return typeof(Single);
772                     case SqlBuffer.StorageType.String:          return typeof(String);
773                     case SqlBuffer.StorageType.SqlBinary:       return typeof(Byte[]);
774                     case SqlBuffer.StorageType.SqlCachedBuffer: return typeof(string);
775                     case SqlBuffer.StorageType.SqlGuid:         return typeof(Guid);
776                     case SqlBuffer.StorageType.SqlXml:          return typeof(string);
777                 }
778             }
779 
780             return null; // need to return the value as an object of some CLS type
781         }
782 
CreateBufferArray(int length)783         internal static SqlBuffer[] CreateBufferArray(int length) {
784             SqlBuffer[] buffers = new SqlBuffer[length];
785             for(int i = 0; i < buffers.Length; ++i) {
786                 buffers[i] = new SqlBuffer();
787             }
788             return buffers;
789         }
790 
CloneBufferArray(SqlBuffer[] values)791         internal static SqlBuffer[] CloneBufferArray(SqlBuffer[] values) {
792             SqlBuffer[] copy = new SqlBuffer[values.Length];
793             for (int i=0; i<values.Length; i++) {
794                 copy[i] = new SqlBuffer(values[i]);
795             }
796             return copy;
797         }
798 
Clear(SqlBuffer[] values)799         internal static void Clear(SqlBuffer[] values) {
800             if (null != values) {
801                 for(int i = 0; i < values.Length; ++i) {
802                     values[i].Clear();
803                 }
804             }
805         }
806 
Clear()807         internal void Clear() {
808             _isNull = false;
809             _type = StorageType.Empty;
810             _object = null;
811         }
812 
SetToDateTime(int daypart, int timepart)813         internal void SetToDateTime(int daypart, int timepart) {
814             Debug.Assert (IsEmpty, "setting value a second time?");
815             _value._dateTimeInfo.daypart = daypart;
816             _value._dateTimeInfo.timepart = timepart;
817             _type = StorageType.DateTime;
818             _isNull = false;
819         }
820 
SetToDecimal(byte precision, byte scale, bool positive, int[] bits)821         internal void SetToDecimal(byte precision, byte scale, bool positive, int[] bits) {
822             Debug.Assert (IsEmpty, "setting value a second time?");
823             _value._numericInfo.precision = precision;
824             _value._numericInfo.scale = scale;
825             _value._numericInfo.positive = positive;
826             _value._numericInfo.data1 = bits[0];
827             _value._numericInfo.data2 = bits[1];
828             _value._numericInfo.data3 = bits[2];
829             _value._numericInfo.data4 = bits[3];
830             _type = StorageType.Decimal;
831             _isNull = false;
832         }
833 
SetToMoney(long value)834         internal void SetToMoney(long value) {
835             Debug.Assert (IsEmpty, "setting value a second time?");
836             _value._int64 = value;
837             _type = StorageType.Money;
838             _isNull = false;
839         }
840 
SetToNullOfType(StorageType storageType)841         internal void SetToNullOfType(StorageType storageType) {
842             Debug.Assert (IsEmpty, "setting value a second time?");
843             _type = storageType;
844             _isNull = true;
845             _object = null;
846         }
847 
SetToString(string value)848         internal void SetToString(string value) {
849             Debug.Assert (IsEmpty, "setting value a second time?");
850             _object = value;
851             _type = StorageType.String;
852             _isNull = false;
853         }
854 
SetToDate(byte[] bytes)855         internal void SetToDate(byte[] bytes) {
856             Debug.Assert(IsEmpty, "setting value a second time?");
857 
858             _type = StorageType.Date;
859             _value._int32 = GetDateFromByteArray(bytes, 0);
860             _isNull = false;
861         }
862 
SetToDate(DateTime date)863         internal void SetToDate(DateTime date) {
864             Debug.Assert(IsEmpty, "setting value a second time?");
865 
866             _type = StorageType.Date;
867             _value._int32 = date.Subtract(DateTime.MinValue).Days;
868             _isNull = false;
869         }
870 
SetToTime(byte[] bytes, int length, byte scale, byte denormalizedScale)871         internal void SetToTime(byte[] bytes, int length, byte scale, byte denormalizedScale) {
872             Debug.Assert(IsEmpty, "setting value a second time?");
873 
874             _type = StorageType.Time;
875             FillInTimeInfo(ref _value._timeInfo, bytes, length, scale, denormalizedScale);
876             _isNull = false;
877         }
878 
SetToTime(TimeSpan timeSpan, byte scale)879         internal void SetToTime(TimeSpan timeSpan, byte scale) {
880             Debug.Assert(IsEmpty, "setting value a second time?");
881 
882             _type = StorageType.Time;
883             _value._timeInfo.ticks = timeSpan.Ticks;
884             _value._timeInfo.scale = scale;
885             _isNull = false;
886         }
887 
SetToDateTime2(byte[] bytes, int length, byte scale, byte denormalizedScale)888         internal void SetToDateTime2(byte[] bytes, int length, byte scale, byte denormalizedScale) {
889             Debug.Assert(IsEmpty, "setting value a second time?");
890 
891             _type = StorageType.DateTime2;
892             FillInTimeInfo(ref _value._dateTime2Info.timeInfo, bytes, length - 3, scale, denormalizedScale); // remaining 3 bytes is for date
893             _value._dateTime2Info.date = GetDateFromByteArray(bytes, length - 3); // 3 bytes for date
894             _isNull = false;
895         }
896 
SetToDateTime2(DateTime dateTime, byte scale)897         internal void SetToDateTime2(DateTime dateTime, byte scale) {
898             Debug.Assert(IsEmpty, "setting value a second time?");
899 
900             _type = StorageType.DateTime2;
901             _value._dateTime2Info.timeInfo.ticks = dateTime.TimeOfDay.Ticks;
902             _value._dateTime2Info.timeInfo.scale = scale;
903             _value._dateTime2Info.date = dateTime.Subtract(DateTime.MinValue).Days;
904             _isNull = false;
905         }
906 
SetToDateTimeOffset(byte[] bytes, int length, byte scale, byte denormalizedScale)907         internal void SetToDateTimeOffset(byte[] bytes, int length, byte scale, byte denormalizedScale) {
908             Debug.Assert(IsEmpty, "setting value a second time?");
909 
910             _type = StorageType.DateTimeOffset;
911             FillInTimeInfo(ref _value._dateTimeOffsetInfo.dateTime2Info.timeInfo, bytes, length - 5, scale, denormalizedScale); // remaining 5 bytes are for date and offset
912             _value._dateTimeOffsetInfo.dateTime2Info.date = GetDateFromByteArray(bytes, length - 5); // 3 bytes for date
913             _value._dateTimeOffsetInfo.offset = (Int16)(bytes[length - 2] + (bytes[length - 1] << 8)); // 2 bytes for offset (Int16)
914             _isNull = false;
915         }
916 
SetToDateTimeOffset(DateTimeOffset dateTimeOffset, byte scale)917         internal void SetToDateTimeOffset(DateTimeOffset dateTimeOffset, byte scale) {
918             Debug.Assert(IsEmpty, "setting value a second time?");
919 
920             _type = StorageType.DateTimeOffset;
921             DateTime utcDateTime = dateTimeOffset.UtcDateTime; // timeInfo stores the utc datetime of a datatimeoffset
922             _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.ticks = utcDateTime.TimeOfDay.Ticks;
923             _value._dateTimeOffsetInfo.dateTime2Info.timeInfo.scale = scale;
924             _value._dateTimeOffsetInfo.dateTime2Info.date = utcDateTime.Subtract(DateTime.MinValue).Days;
925             _value._dateTimeOffsetInfo.offset = (Int16)dateTimeOffset.Offset.TotalMinutes;
926             _isNull = false;
927         }
928 
FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int length, byte scale, byte denormalizedScale)929         private static void FillInTimeInfo(ref TimeInfo timeInfo, byte[] timeBytes, int length, byte scale, byte denormalizedScale) {
930             Debug.Assert(3 <= length && length <= 5, "invalid data length for timeInfo: " + length);
931             Debug.Assert(0 <= scale && scale <= 7, "invalid scale: " + scale);
932             Debug.Assert(0 <= denormalizedScale && denormalizedScale <= 7, "invalid denormalized scale: " + denormalizedScale);
933 
934             Int64 tickUnits = (Int64)timeBytes[0] + ((Int64)timeBytes[1] << 8) + ((Int64)timeBytes[2] << 16);
935             if (length > 3) {
936                 tickUnits += ((Int64)timeBytes[3] << 24);
937             }
938             if (length > 4) {
939                 tickUnits += ((Int64)timeBytes[4] << 32);
940             }
941             timeInfo.ticks = tickUnits * TdsEnums.TICKS_FROM_SCALE[scale];
942 
943             // Once the deserialization has been completed using the value scale, we need to set the actual denormalized scale,
944             // coming from the data type, on the original result, so that it has the proper scale setting.
945             // This only applies for values that got serialized/deserialized for encryption. Otherwise, both scales should be equal.
946             timeInfo.scale = denormalizedScale;
947         }
948 
GetDateFromByteArray(byte[] buf, int offset)949         private static Int32 GetDateFromByteArray(byte[] buf, int offset) {
950             return buf[offset] + (buf[offset + 1] << 8) + (buf[offset + 2] << 16);
951         }
952 
ThrowIfNull()953         private void ThrowIfNull() {
954             if (IsNull) {
955                 throw new SqlNullValueException();
956             }
957         }
958     }
959 }// namespace
960