1 //------------------------------------------------------------------------------
2 // <copyright file="DataStorage.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 //------------------------------------------------------------------------------
9 namespace System.Data.Common {
11 using System.Collections;
12 using System.Collections.Concurrent;
13 using System.Collections.Generic;
14 using System.Data.SqlTypes;
15 using System.Diagnostics;
17 using System.Xml.Serialization;
19 internal enum StorageType {
20 Empty = TypeCode.Empty, // 0
21 Object = TypeCode.Object,
22 DBNull = TypeCode.DBNull,
23 Boolean = TypeCode.Boolean,
25 SByte = TypeCode.SByte,
27 Int16 = TypeCode.Int16,
28 UInt16 = TypeCode.UInt16,
29 Int32 = TypeCode.Int32,
30 UInt32 = TypeCode.UInt32,
31 Int64 = TypeCode.Int64,
32 UInt64 = TypeCode.UInt64,
33 Single = TypeCode.Single,
34 Double = TypeCode.Double,
35 Decimal = TypeCode.Decimal, // 15
36 DateTime = TypeCode.DateTime, // 16
38 String = TypeCode.String, // 18
48 SqlBinary, // SqlTypes should remain at the end for IsSqlType checking
66 abstract internal class DataStorage {
68 // for Whidbey 40426, searching down the Type[] is about 20% faster than using a Dictionary
69 // must keep in same order as enum StorageType
70 private static readonly Type[] StorageClassType = new Type[] {
95 typeof(DateTimeOffset),
96 typeof(System.Numerics.BigInteger),
117 internal readonly DataColumn Column;
118 internal readonly DataTable Table;
119 internal readonly Type DataType;
120 internal readonly StorageType StorageTypeCode;
121 private System.Collections.BitArray dbNullBits;
123 private readonly object DefaultValue;
124 internal readonly object NullValue;
126 internal readonly bool IsCloneable;
127 internal readonly bool IsCustomDefinedType;
128 internal readonly bool IsStringType;
129 internal readonly bool IsValueType;
131 private readonly static Func<Type, Tuple<bool, bool, bool, bool>> _inspectTypeForInterfaces = InspectTypeForInterfaces;
132 private readonly static ConcurrentDictionary<Type, Tuple<bool, bool, bool, bool>> _typeImplementsInterface = new ConcurrentDictionary<Type, Tuple<bool, bool, bool, bool>>();
134 protected DataStorage(DataColumn column, Type type, object defaultValue, StorageType storageType)
135 : this(column, type, defaultValue, DBNull.Value, false, storageType) {
138 protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, StorageType storageType)
139 : this(column, type, defaultValue, nullValue, false, storageType) {
142 protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, bool isICloneable, StorageType storageType) {
143 Debug.Assert(storageType == GetStorageType(type), "Incorrect storage type specified");
145 Table = column.Table;
147 StorageTypeCode = storageType;
148 DefaultValue = defaultValue;
149 NullValue = nullValue;
150 IsCloneable = isICloneable;
151 IsCustomDefinedType = IsTypeCustomType(StorageTypeCode);
152 IsStringType = ((StorageType.String == StorageTypeCode) || (StorageType.SqlString == StorageTypeCode));
153 IsValueType = DetermineIfValueType(StorageTypeCode, type);
156 internal DataSetDateTime DateTimeMode {
158 return Column.DateTimeMode;
162 internal IFormatProvider FormatProvider {
164 return Table.FormatProvider;
168 public virtual Object Aggregate(int[] recordNos, AggregateType kind) {
169 if (AggregateType.Count == kind) {
170 return this.AggregateCount(recordNos);
175 public object AggregateCount(int[] recordNos) {
177 for (int i = 0; i < recordNos.Length; i++) {
178 if (!this.dbNullBits.Get(recordNos[i]))
184 protected int CompareBits(int recordNo1, int recordNo2) {
185 bool recordNo1Null = this.dbNullBits.Get(recordNo1);
186 bool recordNo2Null = this.dbNullBits.Get(recordNo2);
187 if (recordNo1Null ^ recordNo2Null) {
197 public abstract int Compare(int recordNo1, int recordNo2);
199 // only does comparision, expect value to be of the correct type
200 public abstract int CompareValueTo(int recordNo1, object value);
202 // only does conversion with support for reference null
203 public virtual object ConvertValue(object value) {
207 protected void CopyBits(int srcRecordNo, int dstRecordNo) {
208 this.dbNullBits.Set(dstRecordNo, this.dbNullBits.Get(srcRecordNo));
211 abstract public void Copy(int recordNo1, int recordNo2);
213 abstract public Object Get(int recordNo);
215 protected Object GetBits(int recordNo) {
216 if (this.dbNullBits.Get(recordNo)) {
222 virtual public int GetStringLength(int record) {
223 System.Diagnostics.Debug.Assert(false, "not a String or SqlString column");
224 return Int32.MaxValue;
227 protected bool HasValue(int recordNo) {
228 return !this.dbNullBits.Get(recordNo);
231 public virtual bool IsNull(int recordNo) {
232 return this.dbNullBits.Get(recordNo);
235 // convert (may not support reference null) and store the value
236 abstract public void Set(int recordNo, Object value);
238 protected void SetNullBit(int recordNo, bool flag) {
239 this.dbNullBits.Set(recordNo, flag);
242 virtual public void SetCapacity(int capacity) {
243 if (null == this.dbNullBits) {
244 this.dbNullBits = new BitArray(capacity);
247 this.dbNullBits.Length = capacity;
251 abstract public object ConvertXmlToObject(string s);
252 public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) {
253 return ConvertXmlToObject(xmlReader.Value);
256 abstract public string ConvertObjectToXml(object value);
257 public virtual void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) {
258 xmlWriter.WriteString(ConvertObjectToXml(value));// should it be NO OP?
261 public static DataStorage CreateStorage(DataColumn column, Type dataType, StorageType typeCode) {
262 Debug.Assert(typeCode == GetStorageType(dataType), "Incorrect storage type specified");
263 if ((StorageType.Empty == typeCode) && (null != dataType)) {
264 if (typeof(INullable).IsAssignableFrom(dataType)) { // Udt, OracleTypes
265 return new SqlUdtStorage(column, dataType);
268 return new ObjectStorage(column, dataType); // non-nullable non-primitives
273 case StorageType.Empty: throw ExceptionBuilder.InvalidStorageType(TypeCode.Empty);
274 case StorageType.DBNull: throw ExceptionBuilder.InvalidStorageType(TypeCode.DBNull);
275 case StorageType.Object: return new ObjectStorage(column, dataType);
276 case StorageType.Boolean: return new BooleanStorage(column);
277 case StorageType.Char: return new CharStorage(column);
278 case StorageType.SByte: return new SByteStorage(column);
279 case StorageType.Byte: return new ByteStorage(column);
280 case StorageType.Int16: return new Int16Storage(column);
281 case StorageType.UInt16: return new UInt16Storage(column);
282 case StorageType.Int32: return new Int32Storage(column);
283 case StorageType.UInt32: return new UInt32Storage(column);
284 case StorageType.Int64: return new Int64Storage(column);
285 case StorageType.UInt64: return new UInt64Storage(column);
286 case StorageType.Single: return new SingleStorage(column);
287 case StorageType.Double: return new DoubleStorage(column);
288 case StorageType.Decimal: return new DecimalStorage(column);
289 case StorageType.DateTime: return new DateTimeStorage(column);
290 case StorageType.TimeSpan: return new TimeSpanStorage(column);
291 case StorageType.String: return new StringStorage(column);
292 case StorageType.Guid: return new ObjectStorage(column, dataType);
294 case StorageType.ByteArray: return new ObjectStorage(column, dataType);
295 case StorageType.CharArray: return new ObjectStorage(column, dataType);
296 case StorageType.Type: return new ObjectStorage(column, dataType);
297 case StorageType.DateTimeOffset: return new DateTimeOffsetStorage(column);
298 case StorageType.BigInteger: return new BigIntegerStorage(column);
299 case StorageType.Uri: return new ObjectStorage(column, dataType);
301 case StorageType.SqlBinary: return new SqlBinaryStorage(column);
302 case StorageType.SqlBoolean: return new SqlBooleanStorage(column);
303 case StorageType.SqlByte: return new SqlByteStorage(column);
304 case StorageType.SqlBytes: return new SqlBytesStorage(column);
305 case StorageType.SqlChars: return new SqlCharsStorage(column);
306 case StorageType.SqlDateTime: return new SqlDateTimeStorage(column); //???/ what to do
307 case StorageType.SqlDecimal: return new SqlDecimalStorage(column);
308 case StorageType.SqlDouble: return new SqlDoubleStorage(column);
309 case StorageType.SqlGuid: return new SqlGuidStorage(column);
310 case StorageType.SqlInt16: return new SqlInt16Storage(column);
311 case StorageType.SqlInt32: return new SqlInt32Storage(column);
312 case StorageType.SqlInt64: return new SqlInt64Storage(column);
313 case StorageType.SqlMoney: return new SqlMoneyStorage(column);
314 case StorageType.SqlSingle: return new SqlSingleStorage(column);
315 case StorageType.SqlString: return new SqlStringStorage(column);
316 // case StorageType.SqlXml: return new SqlXmlStorage(column);
319 System.Diagnostics.Debug.Assert(false, "shouldn't be here");
320 goto case StorageType.Object;
324 internal static StorageType GetStorageType(Type dataType) {
325 for (int i = 0; i < StorageClassType.Length; ++i) {
326 if (dataType == StorageClassType[i]) {
327 return (StorageType)i;
330 TypeCode tcode = Type.GetTypeCode(dataType);
331 if (TypeCode.Object != tcode) { // enum -> Int64/Int32/Int16/Byte
332 return (StorageType)tcode;
334 return StorageType.Empty;
337 internal static Type GetTypeStorage(StorageType storageType) {
338 return StorageClassType[(int)storageType];
341 internal static bool IsTypeCustomType(Type type) {
342 return IsTypeCustomType(GetStorageType(type));
345 internal static bool IsTypeCustomType(StorageType typeCode) {
346 return ((StorageType.Object == typeCode) || (StorageType.Empty == typeCode) || (StorageType.CharArray == typeCode));
349 internal static bool IsSqlType(StorageType storageType) {
350 return (StorageType.SqlBinary <= storageType);
353 public static bool IsSqlType(Type dataType) {
354 for (int i = (int)StorageType.SqlBinary; i < StorageClassType.Length; ++i) {
355 if (dataType == StorageClassType[i]) {
362 private static bool DetermineIfValueType(StorageType typeCode, Type dataType) {
365 case StorageType.Boolean:
366 case StorageType.Char:
367 case StorageType.SByte:
368 case StorageType.Byte:
369 case StorageType.Int16:
370 case StorageType.UInt16:
371 case StorageType.Int32:
372 case StorageType.UInt32:
373 case StorageType.Int64:
374 case StorageType.UInt64:
375 case StorageType.Single:
376 case StorageType.Double:
377 case StorageType.Decimal:
378 case StorageType.DateTime:
379 case StorageType.DateTimeOffset:
380 case StorageType.BigInteger:
381 case StorageType.TimeSpan:
382 case StorageType.Guid:
383 case StorageType.SqlBinary:
384 case StorageType.SqlBoolean:
385 case StorageType.SqlByte:
386 case StorageType.SqlDateTime:
387 case StorageType.SqlDecimal:
388 case StorageType.SqlDouble:
389 case StorageType.SqlGuid:
390 case StorageType.SqlInt16:
391 case StorageType.SqlInt32:
392 case StorageType.SqlInt64:
393 case StorageType.SqlMoney:
394 case StorageType.SqlSingle:
395 case StorageType.SqlString:
399 case StorageType.String:
400 case StorageType.ByteArray:
401 case StorageType.CharArray:
402 case StorageType.Type:
403 case StorageType.Uri:
404 case StorageType.SqlBytes:
405 case StorageType.SqlChars:
410 result = dataType.IsValueType;
413 Debug.Assert(result == dataType.IsValueType, "typeCode mismatches dataType");
417 internal static void ImplementsInterfaces(
418 StorageType typeCode,
422 out bool xmlSerializable,
423 out bool changeTracking,
424 out bool revertibleChangeTracking)
426 Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType");
427 if (IsSqlType(typeCode)) {
430 changeTracking = false;
431 revertibleChangeTracking = false;
432 xmlSerializable = true;
434 else if (StorageType.Empty != typeCode) {
437 changeTracking = false;
438 revertibleChangeTracking = false;
439 xmlSerializable = false;
442 // Non-standard type - look it up in the dictionary or add it if not found
443 Tuple<bool, bool, bool, bool> interfaces = _typeImplementsInterface.GetOrAdd(dataType, _inspectTypeForInterfaces);
445 nullable = interfaces.Item1;
446 changeTracking = interfaces.Item2;
447 revertibleChangeTracking = interfaces.Item3;
448 xmlSerializable = interfaces.Item4;
450 Debug.Assert(nullable == typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType), "INullable");
451 Debug.Assert(changeTracking == typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType), "IChangeTracking");
452 Debug.Assert(revertibleChangeTracking == typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType), "IRevertibleChangeTracking");
453 Debug.Assert(xmlSerializable == typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType), "IXmlSerializable");
456 private static Tuple<bool, bool, bool, bool> InspectTypeForInterfaces(Type dataType) {
457 Debug.Assert(dataType != null, "Type should not be null");
459 return new Tuple<bool,bool,bool,bool>(
460 typeof(System.Data.SqlTypes.INullable).IsAssignableFrom(dataType),
461 typeof(System.ComponentModel.IChangeTracking).IsAssignableFrom(dataType),
462 typeof(System.ComponentModel.IRevertibleChangeTracking).IsAssignableFrom(dataType),
463 typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(dataType));
466 internal static bool ImplementsINullableValue(StorageType typeCode, Type dataType) {
467 Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType");
468 return ((StorageType.Empty == typeCode) && dataType.IsGenericType && (dataType.GetGenericTypeDefinition() == typeof(System.Nullable<>)));
471 public static bool IsObjectNull(object value) {
472 return ((null == value) || (DBNull.Value == value) || IsObjectSqlNull(value));
475 public static bool IsObjectSqlNull(object value) {
476 INullable inullable = (value as INullable);
477 return ((null != inullable) && inullable.IsNull);
480 internal object GetEmptyStorageInternal(int recordCount) {
481 return GetEmptyStorage(recordCount);
484 internal void CopyValueInternal(int record, object store, BitArray nullbits, int storeIndex) {
485 CopyValue(record, store, nullbits, storeIndex);
488 internal void SetStorageInternal(object store, BitArray nullbits) {
489 SetStorage(store, nullbits);
492 abstract protected Object GetEmptyStorage(int recordCount);
493 abstract protected void CopyValue(int record, object store, BitArray nullbits, int storeIndex);
494 abstract protected void SetStorage(object store, BitArray nullbits);
495 protected void SetNullStorage(BitArray nullbits) {
496 dbNullBits = nullbits;
499 /// <summary>wrapper around Type.GetType</summary>
500 /// <param name="value">assembly qualified type name or one of the special known types</param>
501 /// <returns>Type or null if not found</returns>
502 /// <exception cref="InvalidOperationException">when type implements IDynamicMetaObjectProvider and not IXmlSerializable</exception>
504 /// Types like "System.Guid" will load regardless of AssemblyQualifiedName because they are special
505 /// Types like "System.Data.SqlTypes.SqlString" will load because they are in the same assembly as this code
506 /// Types like "System.Numerics.BigInteger" won't load because they are not special and not same assembly as this code
508 internal static Type GetType(string value) {
509 Type dataType = Type.GetType(value); // throwOnError=false, ignoreCase=fase
510 if (null == dataType) {
511 if ("System.Numerics.BigInteger" == value) {
512 dataType = typeof(System.Numerics.BigInteger);
516 // Dev10 671061: prevent reading type from schema which implements IDynamicMetaObjectProvider and not IXmlSerializable
517 // the check here prevents the type from being loaded in schema or as instance data (when DataType is object)
518 ObjectStorage.VerifyIDynamicMetaObjectProvider(dataType);
522 /// <summary>wrapper around Type.AssemblyQualifiedName</summary>
523 /// <param name="type"></param>
524 /// <returns>qualified name when writing in xml</returns>
525 /// <exception cref="InvalidOperationException">when type implements IDynamicMetaObjectProvider and not IXmlSerializable</exception>
526 internal static string GetQualifiedName(Type type)
528 Debug.Assert(null != type, "null type");
529 ObjectStorage.VerifyIDynamicMetaObjectProvider(type);
530 return type.AssemblyQualifiedName;