//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ namespace System.Data.Common { using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.SqlTypes; using System.Diagnostics; using System.Xml; using System.Xml.Serialization; internal enum StorageType { Empty = TypeCode.Empty, // 0 Object = TypeCode.Object, DBNull = TypeCode.DBNull, Boolean = TypeCode.Boolean, Char = TypeCode.Char, SByte = TypeCode.SByte, Byte = TypeCode.Byte, Int16 = TypeCode.Int16, UInt16 = TypeCode.UInt16, Int32 = TypeCode.Int32, UInt32 = TypeCode.UInt32, Int64 = TypeCode.Int64, UInt64 = TypeCode.UInt64, Single = TypeCode.Single, Double = TypeCode.Double, Decimal = TypeCode.Decimal, // 15 DateTime = TypeCode.DateTime, // 16 TimeSpan = 17, String = TypeCode.String, // 18 Guid = 19, ByteArray = 20, CharArray = 21, Type = 22, DateTimeOffset = 23, BigInteger = 24, Uri = 25, SqlBinary, // SqlTypes should remain at the end for IsSqlType checking SqlBoolean, SqlByte, SqlBytes, SqlChars, SqlDateTime, SqlDecimal, SqlDouble, SqlGuid, SqlInt16, SqlInt32, SqlInt64, SqlMoney, SqlSingle, SqlString, // SqlXml, }; abstract internal class DataStorage { // for Whidbey 40426, searching down the Type[] is about 20% faster than using a Dictionary // must keep in same order as enum StorageType private static readonly Type[] StorageClassType = new Type[] { null, typeof(Object), typeof(DBNull), typeof(Boolean), typeof(Char), typeof(SByte), typeof(Byte), typeof(Int16), typeof(UInt16), typeof(Int32), typeof(UInt32), typeof(Int64), typeof(UInt64), typeof(Single), typeof(Double), typeof(Decimal), typeof(DateTime), typeof(TimeSpan), typeof(String), typeof(Guid), typeof(byte[]), typeof(char[]), typeof(Type), typeof(DateTimeOffset), typeof(System.Numerics.BigInteger), typeof(Uri), typeof(SqlBinary), typeof(SqlBoolean), typeof(SqlByte), typeof(SqlBytes), typeof(SqlChars), typeof(SqlDateTime), typeof(SqlDecimal), typeof(SqlDouble), typeof(SqlGuid), typeof(SqlInt16), typeof(SqlInt32), typeof(SqlInt64), typeof(SqlMoney), typeof(SqlSingle), typeof(SqlString), // typeof(SqlXml), }; internal readonly DataColumn Column; internal readonly DataTable Table; internal readonly Type DataType; internal readonly StorageType StorageTypeCode; private System.Collections.BitArray dbNullBits; private readonly object DefaultValue; internal readonly object NullValue; internal readonly bool IsCloneable; internal readonly bool IsCustomDefinedType; internal readonly bool IsStringType; internal readonly bool IsValueType; private readonly static Func> _inspectTypeForInterfaces = InspectTypeForInterfaces; private readonly static ConcurrentDictionary> _typeImplementsInterface = new ConcurrentDictionary>(); protected DataStorage(DataColumn column, Type type, object defaultValue, StorageType storageType) : this(column, type, defaultValue, DBNull.Value, false, storageType) { } protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, StorageType storageType) : this(column, type, defaultValue, nullValue, false, storageType) { } protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, bool isICloneable, StorageType storageType) { Debug.Assert(storageType == GetStorageType(type), "Incorrect storage type specified"); Column = column; Table = column.Table; DataType = type; StorageTypeCode = storageType; DefaultValue = defaultValue; NullValue = nullValue; IsCloneable = isICloneable; IsCustomDefinedType = IsTypeCustomType(StorageTypeCode); IsStringType = ((StorageType.String == StorageTypeCode) || (StorageType.SqlString == StorageTypeCode)); IsValueType = DetermineIfValueType(StorageTypeCode, type); } internal DataSetDateTime DateTimeMode { get { return Column.DateTimeMode; } } internal IFormatProvider FormatProvider { get { return Table.FormatProvider; } } public virtual Object Aggregate(int[] recordNos, AggregateType kind) { if (AggregateType.Count == kind) { return this.AggregateCount(recordNos); } return null; } public object AggregateCount(int[] recordNos) { int count = 0; for (int i = 0; i < recordNos.Length; i++) { if (!this.dbNullBits.Get(recordNos[i])) count++; } return count; } protected int CompareBits(int recordNo1, int recordNo2) { bool recordNo1Null = this.dbNullBits.Get(recordNo1); bool recordNo2Null = this.dbNullBits.Get(recordNo2); if (recordNo1Null ^ recordNo2Null) { if (recordNo1Null) return -1; else return 1; } return 0; } public abstract int Compare(int recordNo1, int recordNo2); // only does comparision, expect value to be of the correct type public abstract int CompareValueTo(int recordNo1, object value); // only does conversion with support for reference null public virtual object ConvertValue(object value) { return value; } protected void CopyBits(int srcRecordNo, int dstRecordNo) { this.dbNullBits.Set(dstRecordNo, this.dbNullBits.Get(srcRecordNo)); } abstract public void Copy(int recordNo1, int recordNo2); abstract public Object Get(int recordNo); protected Object GetBits(int recordNo) { if (this.dbNullBits.Get(recordNo)) { return NullValue; } return DefaultValue; } virtual public int GetStringLength(int record) { System.Diagnostics.Debug.Assert(false, "not a String or SqlString column"); return Int32.MaxValue; } protected bool HasValue(int recordNo) { return !this.dbNullBits.Get(recordNo); } public virtual bool IsNull(int recordNo) { return this.dbNullBits.Get(recordNo); } // convert (may not support reference null) and store the value abstract public void Set(int recordNo, Object value); protected void SetNullBit(int recordNo, bool flag) { this.dbNullBits.Set(recordNo, flag); } virtual public void SetCapacity(int capacity) { if (null == this.dbNullBits) { this.dbNullBits = new BitArray(capacity); } else { this.dbNullBits.Length = capacity; } } abstract public object ConvertXmlToObject(string s); public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) { return ConvertXmlToObject(xmlReader.Value); } abstract public string ConvertObjectToXml(object value); public virtual void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) { xmlWriter.WriteString(ConvertObjectToXml(value));// should it be NO OP? } public static DataStorage CreateStorage(DataColumn column, Type dataType, StorageType typeCode) { Debug.Assert(typeCode == GetStorageType(dataType), "Incorrect storage type specified"); if ((StorageType.Empty == typeCode) && (null != dataType)) { if (typeof(INullable).IsAssignableFrom(dataType)) { // Udt, OracleTypes return new SqlUdtStorage(column, dataType); } else { return new ObjectStorage(column, dataType); // non-nullable non-primitives } } switch (typeCode) { case StorageType.Empty: throw ExceptionBuilder.InvalidStorageType(TypeCode.Empty); case StorageType.DBNull: throw ExceptionBuilder.InvalidStorageType(TypeCode.DBNull); case StorageType.Object: return new ObjectStorage(column, dataType); case StorageType.Boolean: return new BooleanStorage(column); case StorageType.Char: return new CharStorage(column); case StorageType.SByte: return new SByteStorage(column); case StorageType.Byte: return new ByteStorage(column); case StorageType.Int16: return new Int16Storage(column); case StorageType.UInt16: return new UInt16Storage(column); case StorageType.Int32: return new Int32Storage(column); case StorageType.UInt32: return new UInt32Storage(column); case StorageType.Int64: return new Int64Storage(column); case StorageType.UInt64: return new UInt64Storage(column); case StorageType.Single: return new SingleStorage(column); case StorageType.Double: return new DoubleStorage(column); case StorageType.Decimal: return new DecimalStorage(column); case StorageType.DateTime: return new DateTimeStorage(column); case StorageType.TimeSpan: return new TimeSpanStorage(column); case StorageType.String: return new StringStorage(column); case StorageType.Guid: return new ObjectStorage(column, dataType); case StorageType.ByteArray: return new ObjectStorage(column, dataType); case StorageType.CharArray: return new ObjectStorage(column, dataType); case StorageType.Type: return new ObjectStorage(column, dataType); case StorageType.DateTimeOffset: return new DateTimeOffsetStorage(column); case StorageType.BigInteger: return new BigIntegerStorage(column); case StorageType.Uri: return new ObjectStorage(column, dataType); case StorageType.SqlBinary: return new SqlBinaryStorage(column); case StorageType.SqlBoolean: return new SqlBooleanStorage(column); case StorageType.SqlByte: return new SqlByteStorage(column); case StorageType.SqlBytes: return new SqlBytesStorage(column); case StorageType.SqlChars: return new SqlCharsStorage(column); case StorageType.SqlDateTime: return new SqlDateTimeStorage(column); //???/ what to do case StorageType.SqlDecimal: return new SqlDecimalStorage(column); case StorageType.SqlDouble: return new SqlDoubleStorage(column); case StorageType.SqlGuid: return new SqlGuidStorage(column); case StorageType.SqlInt16: return new SqlInt16Storage(column); case StorageType.SqlInt32: return new SqlInt32Storage(column); case StorageType.SqlInt64: return new SqlInt64Storage(column); case StorageType.SqlMoney: return new SqlMoneyStorage(column); case StorageType.SqlSingle: return new SqlSingleStorage(column); case StorageType.SqlString: return new SqlStringStorage(column); // case StorageType.SqlXml: return new SqlXmlStorage(column); default: System.Diagnostics.Debug.Assert(false, "shouldn't be here"); goto case StorageType.Object; } } internal static StorageType GetStorageType(Type dataType) { for (int i = 0; i < StorageClassType.Length; ++i) { if (dataType == StorageClassType[i]) { return (StorageType)i; } } TypeCode tcode = Type.GetTypeCode(dataType); if (TypeCode.Object != tcode) { // enum -> Int64/Int32/Int16/Byte return (StorageType)tcode; } return StorageType.Empty; } internal static Type GetTypeStorage(StorageType storageType) { return StorageClassType[(int)storageType]; } internal static bool IsTypeCustomType(Type type) { return IsTypeCustomType(GetStorageType(type)); } internal static bool IsTypeCustomType(StorageType typeCode) { return ((StorageType.Object == typeCode) || (StorageType.Empty == typeCode) || (StorageType.CharArray == typeCode)); } internal static bool IsSqlType(StorageType storageType) { return (StorageType.SqlBinary <= storageType); } public static bool IsSqlType(Type dataType) { for (int i = (int)StorageType.SqlBinary; i < StorageClassType.Length; ++i) { if (dataType == StorageClassType[i]) { return true; } } return false; } private static bool DetermineIfValueType(StorageType typeCode, Type dataType) { bool result; switch (typeCode) { case StorageType.Boolean: case StorageType.Char: case StorageType.SByte: case StorageType.Byte: case StorageType.Int16: case StorageType.UInt16: case StorageType.Int32: case StorageType.UInt32: case StorageType.Int64: case StorageType.UInt64: case StorageType.Single: case StorageType.Double: case StorageType.Decimal: case StorageType.DateTime: case StorageType.DateTimeOffset: case StorageType.BigInteger: case StorageType.TimeSpan: case StorageType.Guid: case StorageType.SqlBinary: case StorageType.SqlBoolean: case StorageType.SqlByte: case StorageType.SqlDateTime: case StorageType.SqlDecimal: case StorageType.SqlDouble: case StorageType.SqlGuid: case StorageType.SqlInt16: case StorageType.SqlInt32: case StorageType.SqlInt64: case StorageType.SqlMoney: case StorageType.SqlSingle: case StorageType.SqlString: result = true; break; case StorageType.String: case StorageType.ByteArray: case StorageType.CharArray: case StorageType.Type: case StorageType.Uri: case StorageType.SqlBytes: case StorageType.SqlChars: result = false; break; default: result = dataType.IsValueType; break; } Debug.Assert(result == dataType.IsValueType, "typeCode mismatches dataType"); return result; } internal static void ImplementsInterfaces( StorageType typeCode, Type dataType, out bool sqlType, out bool nullable, out bool xmlSerializable, out bool changeTracking, out bool revertibleChangeTracking) { Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType"); if (IsSqlType(typeCode)) { sqlType = true; nullable = true; changeTracking = false; revertibleChangeTracking = false; xmlSerializable = true; } else if (StorageType.Empty != typeCode) { sqlType = false; nullable = false; changeTracking = false; revertibleChangeTracking = false; xmlSerializable = false; } else { // Non-standard type - look it up in the dictionary or add it if not found Tuple interfaces = _typeImplementsInterface.GetOrAdd(dataType, _inspectTypeForInterfaces); sqlType = false; nullable = interfaces.Item1; changeTracking = interfaces.Item2; revertibleChangeTracking = interfaces.Item3; xmlSerializable = interfaces.Item4; } Debug.Assert(nullable == typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType), "INullable"); Debug.Assert(changeTracking == typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType), "IChangeTracking"); Debug.Assert(revertibleChangeTracking == typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType), "IRevertibleChangeTracking"); Debug.Assert(xmlSerializable == typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType), "IXmlSerializable"); } private static Tuple InspectTypeForInterfaces(Type dataType) { Debug.Assert(dataType != null, "Type should not be null"); return new Tuple( typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType), typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType), typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType), typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType)); } internal static bool ImplementsINullableValue(StorageType typeCode, Type dataType) { Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType"); return ((StorageType.Empty == typeCode) && dataType.IsGenericType && (dataType.GetGenericTypeDefinition() == typeof(System.Nullable<>))); } public static bool IsObjectNull(object value) { return ((null == value) || (DBNull.Value == value) || IsObjectSqlNull(value)); } public static bool IsObjectSqlNull(object value) { INullable inullable = (value as INullable); return ((null != inullable) && inullable.IsNull); } internal object GetEmptyStorageInternal(int recordCount) { return GetEmptyStorage(recordCount); } internal void CopyValueInternal(int record, object store, BitArray nullbits, int storeIndex) { CopyValue(record, store, nullbits, storeIndex); } internal void SetStorageInternal(object store, BitArray nullbits) { SetStorage(store, nullbits); } abstract protected Object GetEmptyStorage(int recordCount); abstract protected void CopyValue(int record, object store, BitArray nullbits, int storeIndex); abstract protected void SetStorage(object store, BitArray nullbits); protected void SetNullStorage(BitArray nullbits) { dbNullBits = nullbits; } /// wrapper around Type.GetType /// assembly qualified type name or one of the special known types /// Type or null if not found /// when type implements IDynamicMetaObjectProvider and not IXmlSerializable /// /// Types like "System.Guid" will load regardless of AssemblyQualifiedName because they are special /// Types like "System.Data.SqlTypes.SqlString" will load because they are in the same assembly as this code /// Types like "System.Numerics.BigInteger" won't load because they are not special and not same assembly as this code /// internal static Type GetType(string value) { Type dataType = Type.GetType(value); // throwOnError=false, ignoreCase=fase if (null == dataType) { if ("System.Numerics.BigInteger" == value) { dataType = typeof(System.Numerics.BigInteger); } } // Dev10 671061: prevent reading type from schema which implements IDynamicMetaObjectProvider and not IXmlSerializable // the check here prevents the type from being loaded in schema or as instance data (when DataType is object) ObjectStorage.VerifyIDynamicMetaObjectProvider(dataType); return dataType; } /// wrapper around Type.AssemblyQualifiedName /// /// qualified name when writing in xml /// when type implements IDynamicMetaObjectProvider and not IXmlSerializable internal static string GetQualifiedName(Type type) { Debug.Assert(null != type, "null type"); ObjectStorage.VerifyIDynamicMetaObjectProvider(type); return type.AssemblyQualifiedName; } } }