2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="SqlDataReader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System.Threading ;
using System.Diagnostics ;
using System.Reflection ;
using System ;
using System.Data ;
using System.IO ;
using System.Collections ;
using System.Collections.Specialized ;
using System.Data.Sql ;
using System.Data.SqlTypes ;
using System.Data.Common ;
using System.Data.ProviderBase ;
using System.ComponentModel ;
using System.Globalization ;
using System.Xml ;
using System.Runtime.InteropServices ;
internal sealed class SqlBuffer {
internal enum StorageType {
Empty = 0 ,
Boolean ,
Byte ,
DateTime ,
Decimal ,
Double ,
Int16 ,
Int32 ,
Int64 ,
Money ,
Single ,
String ,
SqlBinary ,
SqlCachedBuffer ,
SqlGuid ,
SqlXml ,
Date ,
DateTime2 ,
DateTimeOffset ,
Time ,
}
internal struct DateTimeInfo {
// This is used to store DateTime
internal Int32 daypart ;
internal Int32 timepart ;
}
internal struct NumericInfo {
// This is used to store Decimal data
internal Int32 data1 ;
internal Int32 data2 ;
internal Int32 data3 ;
internal Int32 data4 ;
internal Byte precision ;
internal Byte scale ;
internal Boolean positive ;
}
internal struct TimeInfo {
internal Int64 ticks ;
internal byte scale ;
}
internal struct DateTime2Info {
internal Int32 date ;
internal TimeInfo timeInfo ;
}
internal struct DateTimeOffsetInfo {
internal DateTime2Info dateTime2Info ;
internal Int16 offset ;
}
[StructLayout(LayoutKind.Explicit)]
internal struct Storage {
[FieldOffset(0)] internal Boolean _boolean ;
[FieldOffset(0)] internal Byte _byte ;
[FieldOffset(0)] internal DateTimeInfo _dateTimeInfo ;
[FieldOffset(0)] internal Double _double ;
[FieldOffset(0)] internal NumericInfo _numericInfo ;
[FieldOffset(0)] internal Int16 _int16 ;
[FieldOffset(0)] internal Int32 _int32 ;
[FieldOffset(0)] internal Int64 _int64 ; // also used to store Money, UtcDateTime, Date , and Time
[FieldOffset(0)] internal Single _single ;
[FieldOffset(0)] internal TimeInfo _timeInfo ;
[FieldOffset(0)] internal DateTime2Info _dateTime2Info ;
[FieldOffset(0)] internal DateTimeOffsetInfo _dateTimeOffsetInfo ;
}
private bool _isNull ;
private StorageType _type ;
private Storage _value ;
private object _object ; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml
internal SqlBuffer ( ) {
}
private SqlBuffer ( SqlBuffer value ) { // Clone
// value types
_isNull = value . _isNull ;
_type = value . _type ;
// ref types - should also be read only unless at some point we allow this data
// to be mutable, then we will need to copy
_value = value . _value ;
_object = value . _object ;
}
internal bool IsEmpty {
get {
return ( StorageType . Empty = = _type ) ;
}
}
internal bool IsNull {
get {
return _isNull ;
}
}
internal StorageType VariantInternalStorageType
{
get { return _type ; }
}
internal Boolean Boolean {
get {
ThrowIfNull ( ) ;
if ( StorageType . Boolean = = _type ) {
return _value . _boolean ;
}
return ( Boolean ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _boolean = value ;
_type = StorageType . Boolean ;
_isNull = false ;
}
}
internal Byte Byte {
get {
ThrowIfNull ( ) ;
if ( StorageType . Byte = = _type ) {
return _value . _byte ;
}
return ( Byte ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _byte = value ;
_type = StorageType . Byte ;
_isNull = false ;
}
}
internal Byte [ ] ByteArray {
get {
ThrowIfNull ( ) ;
return this . SqlBinary . Value ; //
}
}
internal DateTime DateTime {
get {
ThrowIfNull ( ) ;
if ( StorageType . Date = = _type ) {
return DateTime . MinValue . AddDays ( _value . _int32 ) ;
}
if ( StorageType . DateTime2 = = _type ) {
return new DateTime ( GetTicksFromDateTime2Info ( _value . _dateTime2Info ) ) ;
}
if ( StorageType . DateTime = = _type ) {
return SqlDateTime . ToDateTime ( _value . _dateTimeInfo . daypart , _value . _dateTimeInfo . timepart ) ;
}
return ( DateTime ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
internal Decimal Decimal {
get {
ThrowIfNull ( ) ;
if ( StorageType . Decimal = = _type ) {
if ( _value . _numericInfo . data4 ! = 0 | | _value . _numericInfo . scale > 28 ) {
throw new OverflowException ( SQLResource . ConversionOverflowMessage ) ;
}
return new Decimal ( _value . _numericInfo . data1 , _value . _numericInfo . data2 , _value . _numericInfo . data3 , ! _value . _numericInfo . positive , _value . _numericInfo . scale ) ;
}
if ( StorageType . Money = = _type ) {
long l = _value . _int64 ;
bool isNegative = false ;
if ( l < 0 ) {
isNegative = true ;
l = - l ;
}
return new Decimal ( ( int ) ( l & 0xffffffff ) , ( int ) ( l > > 32 ) , 0 , isNegative , 4 ) ;
}
return ( Decimal ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
internal Double Double {
get {
ThrowIfNull ( ) ;
if ( StorageType . Double = = _type ) {
return _value . _double ;
}
return ( Double ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _double = value ;
_type = StorageType . Double ;
_isNull = false ;
}
}
internal Guid Guid {
get {
//
ThrowIfNull ( ) ;
return this . SqlGuid . Value ;
}
}
internal Int16 Int16 {
get {
ThrowIfNull ( ) ;
if ( StorageType . Int16 = = _type ) {
return _value . _int16 ;
}
return ( Int16 ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _int16 = value ;
_type = StorageType . Int16 ;
_isNull = false ;
}
}
internal Int32 Int32 {
get {
ThrowIfNull ( ) ;
if ( StorageType . Int32 = = _type ) {
return _value . _int32 ;
}
return ( Int32 ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _int32 = value ;
_type = StorageType . Int32 ;
_isNull = false ;
}
}
internal Int64 Int64 {
get {
ThrowIfNull ( ) ;
if ( StorageType . Int64 = = _type ) {
return _value . _int64 ;
}
return ( Int64 ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _int64 = value ;
_type = StorageType . Int64 ;
_isNull = false ;
}
}
internal Single Single {
get {
ThrowIfNull ( ) ;
if ( StorageType . Single = = _type ) {
return _value . _single ;
}
return ( Single ) this . Value ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _single = value ;
_type = StorageType . Single ;
_isNull = false ;
}
}
internal String String {
get {
ThrowIfNull ( ) ;
if ( StorageType . String = = _type ) {
return ( String ) _object ;
}
else if ( StorageType . SqlCachedBuffer = = _type ) {
return ( ( SqlCachedBuffer ) ( _object ) ) . ToString ( ) ;
}
return ( String ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
// use static list of format strings indexed by scale for perf!
private static string [ ] __katmaiDateTimeOffsetFormatByScale = new string [ ] {
"yyyy-MM-dd HH:mm:ss zzz" ,
"yyyy-MM-dd HH:mm:ss.f zzz" ,
"yyyy-MM-dd HH:mm:ss.ff zzz" ,
"yyyy-MM-dd HH:mm:ss.fff zzz" ,
"yyyy-MM-dd HH:mm:ss.ffff zzz" ,
"yyyy-MM-dd HH:mm:ss.fffff zzz" ,
"yyyy-MM-dd HH:mm:ss.ffffff zzz" ,
"yyyy-MM-dd HH:mm:ss.fffffff zzz" ,
} ;
private static string [ ] __katmaiDateTime2FormatByScale = new string [ ] {
"yyyy-MM-dd HH:mm:ss" ,
"yyyy-MM-dd HH:mm:ss.f" ,
"yyyy-MM-dd HH:mm:ss.ff" ,
"yyyy-MM-dd HH:mm:ss.fff" ,
"yyyy-MM-dd HH:mm:ss.ffff" ,
"yyyy-MM-dd HH:mm:ss.fffff" ,
"yyyy-MM-dd HH:mm:ss.ffffff" ,
"yyyy-MM-dd HH:mm:ss.fffffff" ,
} ;
private static string [ ] __katmaiTimeFormatByScale = new string [ ] {
"HH:mm:ss" ,
"HH:mm:ss.f" ,
"HH:mm:ss.ff" ,
"HH:mm:ss.fff" ,
"HH:mm:ss.ffff" ,
"HH:mm:ss.fffff" ,
"HH:mm:ss.ffffff" ,
"HH:mm:ss.fffffff" ,
} ;
internal string KatmaiDateTimeString {
get {
ThrowIfNull ( ) ;
if ( StorageType . Date = = _type ) {
return this . DateTime . ToString ( "yyyy-MM-dd" , DateTimeFormatInfo . InvariantInfo ) ;
}
if ( StorageType . Time = = _type ) {
byte scale = _value . _timeInfo . scale ;
return new DateTime ( _value . _timeInfo . ticks ) . ToString ( __katmaiTimeFormatByScale [ scale ] , DateTimeFormatInfo . InvariantInfo ) ;
}
if ( StorageType . DateTime2 = = _type ) {
byte scale = _value . _dateTime2Info . timeInfo . scale ;
return this . DateTime . ToString ( __katmaiDateTime2FormatByScale [ scale ] , DateTimeFormatInfo . InvariantInfo ) ;
}
if ( StorageType . DateTimeOffset = = _type ) {
DateTimeOffset dto = this . DateTimeOffset ;
byte scale = _value . _dateTimeOffsetInfo . dateTime2Info . timeInfo . scale ;
return dto . ToString ( __katmaiDateTimeOffsetFormatByScale [ scale ] , DateTimeFormatInfo . InvariantInfo ) ;
}
return ( String ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlString KatmaiDateTimeSqlString {
get {
if ( StorageType . Date = = _type | |
StorageType . Time = = _type | |
StorageType . DateTime2 = = _type | |
StorageType . DateTimeOffset = = _type ) {
if ( IsNull ) {
return SqlString . Null ;
}
return new SqlString ( KatmaiDateTimeString ) ;
}
return ( SqlString ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal TimeSpan Time {
get {
ThrowIfNull ( ) ;
if ( StorageType . Time = = _type ) {
return new TimeSpan ( _value . _timeInfo . ticks ) ;
}
return ( TimeSpan ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
internal DateTimeOffset DateTimeOffset {
get {
ThrowIfNull ( ) ;
if ( StorageType . DateTimeOffset = = _type ) {
TimeSpan offset = new TimeSpan ( 0 , _value . _dateTimeOffsetInfo . offset , 0 ) ;
// datetime part presents time in UTC
return new DateTimeOffset ( GetTicksFromDateTime2Info ( _value . _dateTimeOffsetInfo . dateTime2Info ) + offset . Ticks , offset ) ;
}
return ( DateTimeOffset ) this . Value ; // anything else we haven't thought of goes through boxing.
}
}
private static long GetTicksFromDateTime2Info ( DateTime2Info dateTime2Info ) {
return ( dateTime2Info . date * TimeSpan . TicksPerDay + dateTime2Info . timeInfo . ticks ) ;
}
internal SqlBinary SqlBinary {
get {
if ( StorageType . SqlBinary = = _type ) {
return ( SqlBinary ) _object ;
}
return ( SqlBinary ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_object = value ;
_type = StorageType . SqlBinary ;
_isNull = value . IsNull ;
}
}
internal SqlBoolean SqlBoolean {
get {
if ( StorageType . Boolean = = _type ) {
if ( IsNull ) {
return SqlBoolean . Null ;
}
return new SqlBoolean ( _value . _boolean ) ;
}
return ( SqlBoolean ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlByte SqlByte {
get {
if ( StorageType . Byte = = _type ) {
if ( IsNull ) {
return SqlByte . Null ;
}
return new SqlByte ( _value . _byte ) ;
}
return ( SqlByte ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlCachedBuffer SqlCachedBuffer {
get {
if ( StorageType . SqlCachedBuffer = = _type ) {
if ( IsNull ) {
return SqlCachedBuffer . Null ;
}
return ( SqlCachedBuffer ) _object ;
}
return ( SqlCachedBuffer ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_object = value ;
_type = StorageType . SqlCachedBuffer ;
_isNull = value . IsNull ;
}
}
internal SqlXml SqlXml {
get {
if ( StorageType . SqlXml = = _type ) {
if ( IsNull ) {
return SqlXml . Null ;
}
return ( SqlXml ) _object ;
}
return ( SqlXml ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_object = value ;
_type = StorageType . SqlXml ;
_isNull = value . IsNull ;
}
}
internal SqlDateTime SqlDateTime {
get {
if ( StorageType . DateTime = = _type ) {
if ( IsNull ) {
return SqlDateTime . Null ;
}
return new SqlDateTime ( _value . _dateTimeInfo . daypart , _value . _dateTimeInfo . timepart ) ;
}
return ( SqlDateTime ) SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlDecimal SqlDecimal {
get {
if ( StorageType . Decimal = = _type ) {
if ( IsNull ) {
return SqlDecimal . Null ;
}
return new SqlDecimal ( _value . _numericInfo . precision ,
_value . _numericInfo . scale ,
_value . _numericInfo . positive ,
_value . _numericInfo . data1 ,
_value . _numericInfo . data2 ,
_value . _numericInfo . data3 ,
_value . _numericInfo . data4
) ;
}
return ( SqlDecimal ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlDouble SqlDouble {
get {
if ( StorageType . Double = = _type ) {
if ( IsNull ) {
return SqlDouble . Null ;
}
return new SqlDouble ( _value . _double ) ;
}
return ( SqlDouble ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlGuid SqlGuid {
get {
if ( StorageType . SqlGuid = = _type ) {
return ( SqlGuid ) _object ;
}
return ( SqlGuid ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
set {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_object = value ;
_type = StorageType . SqlGuid ;
_isNull = value . IsNull ;
}
}
internal SqlInt16 SqlInt16 {
get {
if ( StorageType . Int16 = = _type ) {
if ( IsNull ) {
return SqlInt16 . Null ;
}
return new SqlInt16 ( _value . _int16 ) ;
}
return ( SqlInt16 ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlInt32 SqlInt32 {
get {
if ( StorageType . Int32 = = _type ) {
if ( IsNull ) {
return SqlInt32 . Null ;
}
return new SqlInt32 ( _value . _int32 ) ;
}
return ( SqlInt32 ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlInt64 SqlInt64 {
get {
if ( StorageType . Int64 = = _type ) {
if ( IsNull ) {
return SqlInt64 . Null ;
}
return new SqlInt64 ( _value . _int64 ) ;
}
return ( SqlInt64 ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlMoney SqlMoney {
get {
if ( StorageType . Money = = _type ) {
if ( IsNull ) {
return SqlMoney . Null ;
}
return new SqlMoney ( _value . _int64 , 1 /*ignored*/ ) ;
}
return ( SqlMoney ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlSingle SqlSingle {
get {
if ( StorageType . Single = = _type ) {
if ( IsNull ) {
return SqlSingle . Null ;
}
return new SqlSingle ( _value . _single ) ;
}
return ( SqlSingle ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal SqlString SqlString {
get {
if ( StorageType . String = = _type ) {
if ( IsNull ) {
return SqlString . Null ;
}
return new SqlString ( ( String ) _object ) ;
}
else if ( StorageType . SqlCachedBuffer = = _type ) {
SqlCachedBuffer data = ( SqlCachedBuffer ) ( _object ) ;
if ( data . IsNull ) {
return SqlString . Null ;
}
return data . ToSqlString ( ) ;
}
return ( SqlString ) this . SqlValue ; // anything else we haven't thought of goes through boxing.
}
}
internal object SqlValue {
get {
switch ( _type ) {
case StorageType . Empty : return DBNull . Value ;
case StorageType . Boolean : return SqlBoolean ;
case StorageType . Byte : return SqlByte ;
case StorageType . DateTime : return SqlDateTime ;
case StorageType . Decimal : return SqlDecimal ;
case StorageType . Double : return SqlDouble ;
case StorageType . Int16 : return SqlInt16 ;
case StorageType . Int32 : return SqlInt32 ;
case StorageType . Int64 : return SqlInt64 ;
case StorageType . Money : return SqlMoney ;
case StorageType . Single : return SqlSingle ;
case StorageType . String : return SqlString ;
case StorageType . SqlCachedBuffer :
{
SqlCachedBuffer data = ( SqlCachedBuffer ) ( _object ) ;
if ( data . IsNull ) {
return SqlXml . Null ;
}
return data . ToSqlXml ( ) ;
}
case StorageType . SqlBinary :
case StorageType . SqlGuid :
return _object ;
case StorageType . SqlXml : {
if ( _isNull ) {
return SqlXml . Null ;
}
Debug . Assert ( null ! = _object ) ;
return ( SqlXml ) _object ;
}
case StorageType . Date :
case StorageType . DateTime2 :
if ( _isNull ) {
return DBNull . Value ;
}
return DateTime ;
case StorageType . DateTimeOffset :
if ( _isNull ) {
return DBNull . Value ;
}
return DateTimeOffset ;
case StorageType . Time :
if ( _isNull ) {
return DBNull . Value ;
}
return Time ;
}
return null ; // need to return the value as an object of some SQL type
}
}
internal object Value {
get {
if ( IsNull ) {
return DBNull . Value ;
}
switch ( _type ) {
case StorageType . Empty : return DBNull . Value ;
case StorageType . Boolean : return Boolean ;
case StorageType . Byte : return Byte ;
case StorageType . DateTime : return DateTime ;
case StorageType . Decimal : return Decimal ;
case StorageType . Double : return Double ;
case StorageType . Int16 : return Int16 ;
case StorageType . Int32 : return Int32 ;
case StorageType . Int64 : return Int64 ;
case StorageType . Money : return Decimal ;
case StorageType . Single : return Single ;
case StorageType . String : return String ;
case StorageType . SqlBinary : return ByteArray ;
case StorageType . SqlCachedBuffer :
{
// If we have a CachedBuffer, it's because it's an XMLTYPE column
// and we have to return a string when they're asking for the CLS
// value of the column.
return ( ( SqlCachedBuffer ) ( _object ) ) . ToString ( ) ;
}
case StorageType . SqlGuid : return Guid ;
case StorageType . SqlXml : {
// XMLTYPE columns must be returned as string when asking for the CLS value
SqlXml data = ( SqlXml ) _object ;
string s = data . Value ;
return s ;
}
case StorageType . Date : return DateTime ;
case StorageType . DateTime2 : return DateTime ;
case StorageType . DateTimeOffset : return DateTimeOffset ;
case StorageType . Time : return Time ;
}
return null ; // need to return the value as an object of some CLS type
}
}
internal Type GetTypeFromStorageType ( bool isSqlType ) {
if ( isSqlType ) {
switch ( _type ) {
case SqlBuffer . StorageType . Empty : return null ;
case SqlBuffer . StorageType . Boolean : return typeof ( SqlBoolean ) ;
case SqlBuffer . StorageType . Byte : return typeof ( SqlByte ) ;
case SqlBuffer . StorageType . DateTime : return typeof ( SqlDateTime ) ;
case SqlBuffer . StorageType . Decimal : return typeof ( SqlDecimal ) ;
case SqlBuffer . StorageType . Double : return typeof ( SqlDouble ) ;
case SqlBuffer . StorageType . Int16 : return typeof ( SqlInt16 ) ;
case SqlBuffer . StorageType . Int32 : return typeof ( SqlInt32 ) ;
case SqlBuffer . StorageType . Int64 : return typeof ( SqlInt64 ) ;
case SqlBuffer . StorageType . Money : return typeof ( SqlMoney ) ;
case SqlBuffer . StorageType . Single : return typeof ( SqlSingle ) ;
case SqlBuffer . StorageType . String : return typeof ( SqlString ) ;
case SqlBuffer . StorageType . SqlCachedBuffer : return typeof ( SqlString ) ;
case SqlBuffer . StorageType . SqlBinary : return typeof ( object ) ;
case SqlBuffer . StorageType . SqlGuid : return typeof ( object ) ;
case SqlBuffer . StorageType . SqlXml : return typeof ( SqlXml ) ;
}
}
else { //Is CLR Type
switch ( _type ) {
case SqlBuffer . StorageType . Empty : return null ;
case SqlBuffer . StorageType . Boolean : return typeof ( Boolean ) ;
case SqlBuffer . StorageType . Byte : return typeof ( Byte ) ;
case SqlBuffer . StorageType . DateTime : return typeof ( DateTime ) ;
case SqlBuffer . StorageType . Decimal : return typeof ( Decimal ) ;
case SqlBuffer . StorageType . Double : return typeof ( Double ) ;
case SqlBuffer . StorageType . Int16 : return typeof ( Int16 ) ;
case SqlBuffer . StorageType . Int32 : return typeof ( Int32 ) ;
case SqlBuffer . StorageType . Int64 : return typeof ( Int64 ) ;
case SqlBuffer . StorageType . Money : return typeof ( Decimal ) ;
case SqlBuffer . StorageType . Single : return typeof ( Single ) ;
case SqlBuffer . StorageType . String : return typeof ( String ) ;
case SqlBuffer . StorageType . SqlBinary : return typeof ( Byte [ ] ) ;
case SqlBuffer . StorageType . SqlCachedBuffer : return typeof ( string ) ;
case SqlBuffer . StorageType . SqlGuid : return typeof ( Guid ) ;
case SqlBuffer . StorageType . SqlXml : return typeof ( string ) ;
}
}
return null ; // need to return the value as an object of some CLS type
}
internal static SqlBuffer [ ] CreateBufferArray ( int length ) {
SqlBuffer [ ] buffers = new SqlBuffer [ length ] ;
for ( int i = 0 ; i < buffers . Length ; + + i ) {
buffers [ i ] = new SqlBuffer ( ) ;
}
return buffers ;
}
internal static SqlBuffer [ ] CloneBufferArray ( SqlBuffer [ ] values ) {
SqlBuffer [ ] copy = new SqlBuffer [ values . Length ] ;
for ( int i = 0 ; i < values . Length ; i + + ) {
copy [ i ] = new SqlBuffer ( values [ i ] ) ;
}
return copy ;
}
internal static void Clear ( SqlBuffer [ ] values ) {
if ( null ! = values ) {
for ( int i = 0 ; i < values . Length ; + + i ) {
values [ i ] . Clear ( ) ;
}
}
}
internal void Clear ( ) {
_isNull = false ;
_type = StorageType . Empty ;
_object = null ;
}
internal void SetToDateTime ( int daypart , int timepart ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _dateTimeInfo . daypart = daypart ;
_value . _dateTimeInfo . timepart = timepart ;
_type = StorageType . DateTime ;
_isNull = false ;
}
internal void SetToDecimal ( byte precision , byte scale , bool positive , int [ ] bits ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _numericInfo . precision = precision ;
_value . _numericInfo . scale = scale ;
_value . _numericInfo . positive = positive ;
_value . _numericInfo . data1 = bits [ 0 ] ;
_value . _numericInfo . data2 = bits [ 1 ] ;
_value . _numericInfo . data3 = bits [ 2 ] ;
_value . _numericInfo . data4 = bits [ 3 ] ;
_type = StorageType . Decimal ;
_isNull = false ;
}
internal void SetToMoney ( long value ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_value . _int64 = value ;
_type = StorageType . Money ;
_isNull = false ;
}
internal void SetToNullOfType ( StorageType storageType ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = storageType ;
_isNull = true ;
_object = null ;
}
internal void SetToString ( string value ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_object = value ;
_type = StorageType . String ;
_isNull = false ;
}
internal void SetToDate ( byte [ ] bytes ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . Date ;
_value . _int32 = GetDateFromByteArray ( bytes , 0 ) ;
_isNull = false ;
}
internal void SetToDate ( DateTime date ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . Date ;
_value . _int32 = date . Subtract ( DateTime . MinValue ) . Days ;
_isNull = false ;
}
internal void SetToTime ( byte [ ] bytes , int length , byte scale , byte denormalizedScale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . Time ;
FillInTimeInfo ( ref _value . _timeInfo , bytes , length , scale , denormalizedScale ) ;
_isNull = false ;
}
internal void SetToTime ( TimeSpan timeSpan , byte scale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . Time ;
_value . _timeInfo . ticks = timeSpan . Ticks ;
_value . _timeInfo . scale = scale ;
_isNull = false ;
}
internal void SetToDateTime2 ( byte [ ] bytes , int length , byte scale , byte denormalizedScale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . DateTime2 ;
FillInTimeInfo ( ref _value . _dateTime2Info . timeInfo , bytes , length - 3 , scale , denormalizedScale ) ; // remaining 3 bytes is for date
_value . _dateTime2Info . date = GetDateFromByteArray ( bytes , length - 3 ) ; // 3 bytes for date
_isNull = false ;
}
internal void SetToDateTime2 ( DateTime dateTime , byte scale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . DateTime2 ;
_value . _dateTime2Info . timeInfo . ticks = dateTime . TimeOfDay . Ticks ;
_value . _dateTime2Info . timeInfo . scale = scale ;
_value . _dateTime2Info . date = dateTime . Subtract ( DateTime . MinValue ) . Days ;
_isNull = false ;
}
internal void SetToDateTimeOffset ( byte [ ] bytes , int length , byte scale , byte denormalizedScale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . DateTimeOffset ;
FillInTimeInfo ( ref _value . _dateTimeOffsetInfo . dateTime2Info . timeInfo , bytes , length - 5 , scale , denormalizedScale ) ; // remaining 5 bytes are for date and offset
_value . _dateTimeOffsetInfo . dateTime2Info . date = GetDateFromByteArray ( bytes , length - 5 ) ; // 3 bytes for date
_value . _dateTimeOffsetInfo . offset = ( Int16 ) ( bytes [ length - 2 ] + ( bytes [ length - 1 ] < < 8 ) ) ; // 2 bytes for offset (Int16)
_isNull = false ;
}
internal void SetToDateTimeOffset ( DateTimeOffset dateTimeOffset , byte scale ) {
Debug . Assert ( IsEmpty , "setting value a second time?" ) ;
_type = StorageType . DateTimeOffset ;
DateTime utcDateTime = dateTimeOffset . UtcDateTime ; // timeInfo stores the utc datetime of a datatimeoffset
_value . _dateTimeOffsetInfo . dateTime2Info . timeInfo . ticks = utcDateTime . TimeOfDay . Ticks ;
_value . _dateTimeOffsetInfo . dateTime2Info . timeInfo . scale = scale ;
_value . _dateTimeOffsetInfo . dateTime2Info . date = utcDateTime . Subtract ( DateTime . MinValue ) . Days ;
_value . _dateTimeOffsetInfo . offset = ( Int16 ) dateTimeOffset . Offset . TotalMinutes ;
_isNull = false ;
}
private static void FillInTimeInfo ( ref TimeInfo timeInfo , byte [ ] timeBytes , int length , byte scale , byte denormalizedScale ) {
Debug . Assert ( 3 < = length & & length < = 5 , "invalid data length for timeInfo: " + length ) ;
Debug . Assert ( 0 < = scale & & scale < = 7 , "invalid scale: " + scale ) ;
Debug . Assert ( 0 < = denormalizedScale & & denormalizedScale < = 7 , "invalid denormalized scale: " + denormalizedScale ) ;
Int64 tickUnits = ( Int64 ) timeBytes [ 0 ] + ( ( Int64 ) timeBytes [ 1 ] < < 8 ) + ( ( Int64 ) timeBytes [ 2 ] < < 16 ) ;
if ( length > 3 ) {
tickUnits + = ( ( Int64 ) timeBytes [ 3 ] < < 24 ) ;
}
if ( length > 4 ) {
tickUnits + = ( ( Int64 ) timeBytes [ 4 ] < < 32 ) ;
}
timeInfo . ticks = tickUnits * TdsEnums . TICKS_FROM_SCALE [ scale ] ;
// Once the deserialization has been completed using the value scale, we need to set the actual denormalized scale,
// coming from the data type, on the original result, so that it has the proper scale setting.
// This only applies for values that got serialized/deserialized for encryption. Otherwise, both scales should be equal.
timeInfo . scale = denormalizedScale ;
}
private static Int32 GetDateFromByteArray ( byte [ ] buf , int offset ) {
return buf [ offset ] + ( buf [ offset + 1 ] < < 8 ) + ( buf [ offset + 2 ] < < 16 ) ;
}
private void ThrowIfNull ( ) {
if ( IsNull ) {
throw new SqlNullValueException ( ) ;
}
}
}
} // namespace