1 //------------------------------------------------------------------------------
2 // <copyright file="SqlDataReader.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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 //------------------------------------------------------------------------------
10 namespace System
.Data
.SqlClient
{
11 using System
.Threading
;
12 using System
.Diagnostics
;
13 using System
.Reflection
;
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
;
26 using System
.Runtime
.InteropServices
;
28 internal sealed class SqlBuffer
{
30 internal enum StorageType
{
53 internal struct DateTimeInfo
{
54 // This is used to store DateTime
55 internal Int32 daypart
;
56 internal Int32 timepart
;
59 internal struct NumericInfo
{
60 // This is used to store Decimal data
65 internal Byte precision
;
67 internal Boolean positive
;
70 internal struct TimeInfo
{
75 internal struct DateTime2Info
{
77 internal TimeInfo timeInfo
;
80 internal struct DateTimeOffsetInfo
{
81 internal DateTime2Info dateTime2Info
;
82 internal Int16 offset
;
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
;
101 private bool _isNull
;
102 private StorageType _type
;
103 private Storage _value
;
104 private object _object
; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml
106 internal SqlBuffer() {
109 private SqlBuffer(SqlBuffer
value) { // Clone
111 _isNull
= value._isNull
;
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
;
119 internal bool IsEmpty
{
121 return (StorageType
.Empty
== _type
);
125 internal bool IsNull
{
131 internal StorageType VariantInternalStorageType
133 get { return _type; }
136 internal Boolean Boolean
{
140 if (StorageType
.Boolean
== _type
) {
141 return _value
._boolean
;
143 return (Boolean
)this.Value
; // anything else we haven't thought of goes through boxing.
146 Debug
.Assert (IsEmpty
, "setting value a second time?");
147 _value
._boolean
= value;
148 _type
= StorageType
.Boolean
;
157 if (StorageType
.Byte
== _type
) {
160 return (Byte
)this.Value
; // anything else we haven't thought of goes through boxing.
163 Debug
.Assert (IsEmpty
, "setting value a second time?");
164 _value
._byte
= value;
165 _type
= StorageType
.Byte
;
170 internal Byte
[] ByteArray
{
173 return this.SqlBinary
.Value
; //
177 internal DateTime DateTime
{
181 if (StorageType
.Date
== _type
) {
182 return DateTime
.MinValue
.AddDays(_value
._int32
);
184 if (StorageType
.DateTime2
== _type
) {
185 return new DateTime(GetTicksFromDateTime2Info(_value
._dateTime2Info
));
187 if (StorageType
.DateTime
== _type
) {
188 return SqlDateTime
.ToDateTime(_value
._dateTimeInfo
.daypart
, _value
._dateTimeInfo
.timepart
);
190 return (DateTime
)this.Value
; // anything else we haven't thought of goes through boxing.
194 internal Decimal Decimal
{
198 if (StorageType
.Decimal
== _type
) {
199 if (_value
._numericInfo
.data4
!= 0 || _value
._numericInfo
.scale
> 28) {
200 throw new OverflowException(SQLResource
.ConversionOverflowMessage
);
202 return new Decimal(_value
._numericInfo
.data1
, _value
._numericInfo
.data2
, _value
._numericInfo
.data3
, !_value
._numericInfo
.positive
, _value
._numericInfo
.scale
);
204 if (StorageType
.Money
== _type
) {
205 long l
= _value
._int64
;
206 bool isNegative
= false;
211 return new Decimal((int)(l
& 0xffffffff), (int)(l
>> 32), 0, isNegative
, 4);
213 return (Decimal
)this.Value
; // anything else we haven't thought of goes through boxing.
217 internal Double Double
{
221 if (StorageType
.Double
== _type
) {
222 return _value
._double
;
224 return (Double
)this.Value
; // anything else we haven't thought of goes through boxing.
227 Debug
.Assert (IsEmpty
, "setting value a second time?");
228 _value
._double
= value;
229 _type
= StorageType
.Double
;
238 return this.SqlGuid
.Value
;
242 internal Int16 Int16
{
246 if (StorageType
.Int16
== _type
) {
247 return _value
._int16
;
249 return (Int16
)this.Value
; // anything else we haven't thought of goes through boxing.
252 Debug
.Assert (IsEmpty
, "setting value a second time?");
253 _value
._int16
= value;
254 _type
= StorageType
.Int16
;
259 internal Int32 Int32
{
263 if (StorageType
.Int32
== _type
) {
264 return _value
._int32
;
266 return (Int32
)this.Value
; // anything else we haven't thought of goes through boxing.
269 Debug
.Assert (IsEmpty
, "setting value a second time?");
270 _value
._int32
= value;
271 _type
= StorageType
.Int32
;
276 internal Int64 Int64
{
280 if (StorageType
.Int64
== _type
) {
281 return _value
._int64
;
283 return (Int64
)this.Value
; // anything else we haven't thought of goes through boxing.
286 Debug
.Assert (IsEmpty
, "setting value a second time?");
287 _value
._int64
= value;
288 _type
= StorageType
.Int64
;
293 internal Single Single
{
297 if (StorageType
.Single
== _type
) {
298 return _value
._single
;
300 return (Single
)this.Value
; // anything else we haven't thought of goes through boxing.
303 Debug
.Assert (IsEmpty
, "setting value a second time?");
304 _value
._single
= value;
305 _type
= StorageType
.Single
;
310 internal String String
{
314 if (StorageType
.String
== _type
) {
315 return (String
)_object
;
317 else if (StorageType
.SqlCachedBuffer
== _type
) {
318 return ((SqlCachedBuffer
)(_object
)).ToString();
320 return (String
)this.Value
; // anything else we haven't thought of goes through boxing.
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",
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",
347 private static string[] __katmaiTimeFormatByScale
= new string[] {
358 internal string KatmaiDateTimeString
{
362 if (StorageType
.Date
== _type
) {
363 return this.DateTime
.ToString("yyyy-MM-dd", DateTimeFormatInfo
.InvariantInfo
);
365 if (StorageType
.Time
== _type
) {
366 byte scale
= _value
._timeInfo
.scale
;
367 return new DateTime(_value
._timeInfo
.ticks
).ToString(__katmaiTimeFormatByScale
[scale
], DateTimeFormatInfo
.InvariantInfo
);
369 if (StorageType
.DateTime2
== _type
) {
370 byte scale
= _value
._dateTime2Info
.timeInfo
.scale
;
371 return this.DateTime
.ToString(__katmaiDateTime2FormatByScale
[scale
], DateTimeFormatInfo
.InvariantInfo
);
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
);
378 return (String
)this.Value
; // anything else we haven't thought of goes through boxing.
382 internal SqlString KatmaiDateTimeSqlString
{
384 if (StorageType
.Date
== _type
||
385 StorageType
.Time
== _type
||
386 StorageType
.DateTime2
== _type
||
387 StorageType
.DateTimeOffset
== _type
) {
389 return SqlString
.Null
;
391 return new SqlString(KatmaiDateTimeString
);
393 return (SqlString
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
397 internal TimeSpan Time
{
401 if (StorageType
.Time
== _type
) {
402 return new TimeSpan(_value
._timeInfo
.ticks
);
405 return (TimeSpan
)this.Value
; // anything else we haven't thought of goes through boxing.
409 internal DateTimeOffset DateTimeOffset
{
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
);
419 return (DateTimeOffset
)this.Value
; // anything else we haven't thought of goes through boxing.
423 private static long GetTicksFromDateTime2Info(DateTime2Info dateTime2Info
) {
424 return (dateTime2Info
.date
* TimeSpan
.TicksPerDay
+ dateTime2Info
.timeInfo
.ticks
);
427 internal SqlBinary SqlBinary
{
429 if (StorageType
.SqlBinary
== _type
) {
430 return (SqlBinary
)_object
;
432 return (SqlBinary
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
435 Debug
.Assert (IsEmpty
, "setting value a second time?");
437 _type
= StorageType
.SqlBinary
;
438 _isNull
= value.IsNull
;
442 internal SqlBoolean SqlBoolean
{
444 if (StorageType
.Boolean
== _type
) {
446 return SqlBoolean
.Null
;
448 return new SqlBoolean(_value
._boolean
);
450 return (SqlBoolean
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
454 internal SqlByte SqlByte
{
456 if (StorageType
.Byte
== _type
) {
460 return new SqlByte(_value
._byte
);
462 return (SqlByte
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
466 internal SqlCachedBuffer SqlCachedBuffer
{
468 if (StorageType
.SqlCachedBuffer
== _type
) {
470 return SqlCachedBuffer
.Null
;
472 return (SqlCachedBuffer
)_object
;
474 return (SqlCachedBuffer
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
477 Debug
.Assert (IsEmpty
, "setting value a second time?");
479 _type
= StorageType
.SqlCachedBuffer
;
480 _isNull
= value.IsNull
;
484 internal SqlXml SqlXml
{
486 if (StorageType
.SqlXml
== _type
) {
490 return (SqlXml
)_object
;
492 return (SqlXml
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
495 Debug
.Assert (IsEmpty
, "setting value a second time?");
497 _type
= StorageType
.SqlXml
;
498 _isNull
= value.IsNull
;
502 internal SqlDateTime SqlDateTime
{
504 if (StorageType
.DateTime
== _type
) {
506 return SqlDateTime
.Null
;
508 return new SqlDateTime(_value
._dateTimeInfo
.daypart
, _value
._dateTimeInfo
.timepart
);
510 return (SqlDateTime
)SqlValue
; // anything else we haven't thought of goes through boxing.
514 internal SqlDecimal SqlDecimal
{
516 if (StorageType
.Decimal
== _type
) {
518 return SqlDecimal
.Null
;
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
529 return (SqlDecimal
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
533 internal SqlDouble SqlDouble
{
535 if (StorageType
.Double
== _type
) {
537 return SqlDouble
.Null
;
539 return new SqlDouble(_value
._double
);
541 return (SqlDouble
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
545 internal SqlGuid SqlGuid
{
547 if (StorageType
.SqlGuid
== _type
) {
548 return (SqlGuid
)_object
;
550 return (SqlGuid
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
553 Debug
.Assert (IsEmpty
, "setting value a second time?");
555 _type
= StorageType
.SqlGuid
;
556 _isNull
= value.IsNull
;
560 internal SqlInt16 SqlInt16
{
562 if (StorageType
.Int16
== _type
) {
564 return SqlInt16
.Null
;
566 return new SqlInt16(_value
._int16
);
568 return (SqlInt16
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
572 internal SqlInt32 SqlInt32
{
574 if (StorageType
.Int32
== _type
) {
576 return SqlInt32
.Null
;
578 return new SqlInt32(_value
._int32
);
580 return (SqlInt32
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
584 internal SqlInt64 SqlInt64
{
586 if (StorageType
.Int64
== _type
) {
588 return SqlInt64
.Null
;
590 return new SqlInt64(_value
._int64
);
592 return (SqlInt64
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
596 internal SqlMoney SqlMoney
{
598 if (StorageType
.Money
== _type
) {
600 return SqlMoney
.Null
;
602 return new SqlMoney(_value
._int64
, 1/*ignored*/);
604 return (SqlMoney
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
608 internal SqlSingle SqlSingle
{
610 if (StorageType
.Single
== _type
) {
612 return SqlSingle
.Null
;
614 return new SqlSingle(_value
._single
);
616 return (SqlSingle
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
620 internal SqlString SqlString
{
622 if (StorageType
.String
== _type
) {
624 return SqlString
.Null
;
626 return new SqlString((String
)_object
);
629 else if (StorageType
.SqlCachedBuffer
== _type
) {
630 SqlCachedBuffer data
= (SqlCachedBuffer
)(_object
);
632 return SqlString
.Null
;
634 return data
.ToSqlString();
636 return (SqlString
)this.SqlValue
; // anything else we haven't thought of goes through boxing.
640 internal object SqlValue
{
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
:
657 SqlCachedBuffer data
= (SqlCachedBuffer
)(_object
);
661 return data
.ToSqlXml();
664 case StorageType
.SqlBinary
:
665 case StorageType
.SqlGuid
:
668 case StorageType
.SqlXml
: {
672 Debug
.Assert(null != _object
);
673 return (SqlXml
) _object
;
675 case StorageType
.Date
:
676 case StorageType
.DateTime2
:
681 case StorageType
.DateTimeOffset
:
685 return DateTimeOffset
;
686 case StorageType
.Time
:
692 return null; // need to return the value as an object of some SQL type
696 internal object Value
{
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
:
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();
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
;
729 case StorageType
.Date
: return DateTime
;
730 case StorageType
.DateTime2
: return DateTime
;
731 case StorageType
.DateTimeOffset
: return DateTimeOffset
;
732 case StorageType
.Time
: return Time
;
734 return null; // need to return the value as an object of some CLS type
738 internal Type
GetTypeFromStorageType (bool isSqlType
) {
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
);
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);
780 return null; // need to return the value as an object of some CLS type
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();
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
]);
799 internal static void Clear(SqlBuffer
[] values
) {
800 if (null != values
) {
801 for(int i
= 0; i
< values
.Length
; ++i
) {
807 internal void Clear() {
809 _type
= StorageType
.Empty
;
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
;
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
;
834 internal void SetToMoney(long value) {
835 Debug
.Assert (IsEmpty
, "setting value a second time?");
836 _value
._int64
= value;
837 _type
= StorageType
.Money
;
841 internal void SetToNullOfType(StorageType storageType
) {
842 Debug
.Assert (IsEmpty
, "setting value a second time?");
848 internal void SetToString(string value) {
849 Debug
.Assert (IsEmpty
, "setting value a second time?");
851 _type
= StorageType
.String
;
855 internal void SetToDate(byte[] bytes
) {
856 Debug
.Assert(IsEmpty
, "setting value a second time?");
858 _type
= StorageType
.Date
;
859 _value
._int32
= GetDateFromByteArray(bytes
, 0);
863 internal void SetToDate(DateTime date
) {
864 Debug
.Assert(IsEmpty
, "setting value a second time?");
866 _type
= StorageType
.Date
;
867 _value
._int32
= date
.Subtract(DateTime
.MinValue
).Days
;
871 internal void SetToTime(byte[] bytes
, int length
, byte scale
, byte denormalizedScale
) {
872 Debug
.Assert(IsEmpty
, "setting value a second time?");
874 _type
= StorageType
.Time
;
875 FillInTimeInfo(ref _value
._timeInfo
, bytes
, length
, scale
, denormalizedScale
);
879 internal void SetToTime(TimeSpan timeSpan
, byte scale
) {
880 Debug
.Assert(IsEmpty
, "setting value a second time?");
882 _type
= StorageType
.Time
;
883 _value
._timeInfo
.ticks
= timeSpan
.Ticks
;
884 _value
._timeInfo
.scale
= scale
;
888 internal void SetToDateTime2(byte[] bytes
, int length
, byte scale
, byte denormalizedScale
) {
889 Debug
.Assert(IsEmpty
, "setting value a second time?");
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
897 internal void SetToDateTime2(DateTime dateTime
, byte scale
) {
898 Debug
.Assert(IsEmpty
, "setting value a second time?");
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
;
907 internal void SetToDateTimeOffset(byte[] bytes
, int length
, byte scale
, byte denormalizedScale
) {
908 Debug
.Assert(IsEmpty
, "setting value a second time?");
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)
917 internal void SetToDateTimeOffset(DateTimeOffset dateTimeOffset
, byte scale
) {
918 Debug
.Assert(IsEmpty
, "setting value a second time?");
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
;
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
);
934 Int64 tickUnits
= (Int64
)timeBytes
[0] + ((Int64
)timeBytes
[1] << 8) + ((Int64
)timeBytes
[2] << 16);
936 tickUnits
+= ((Int64
)timeBytes
[3] << 24);
939 tickUnits
+= ((Int64
)timeBytes
[4] << 32);
941 timeInfo
.ticks
= tickUnits
* TdsEnums
.TICKS_FROM_SCALE
[scale
];
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
;
949 private static Int32
GetDateFromByteArray(byte[] buf
, int offset
) {
950 return buf
[offset
] + (buf
[offset
+ 1] << 8) + (buf
[offset
+ 2] << 16);
953 private void ThrowIfNull() {
955 throw new SqlNullValueException();