Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / Common / DataStorage.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataStorage.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 //------------------------------------------------------------------------------
8
9 namespace System.Data.Common {
10     using System;
11     using System.Collections;
12     using System.Collections.Concurrent;
13     using System.Collections.Generic;
14     using System.Data.SqlTypes;
15     using System.Diagnostics;
16     using System.Xml;
17     using System.Xml.Serialization;
18
19     internal enum StorageType {
20         Empty       = TypeCode.Empty, // 0
21         Object      = TypeCode.Object,
22         DBNull      = TypeCode.DBNull,
23         Boolean     = TypeCode.Boolean,
24         Char        = TypeCode.Char,
25         SByte       = TypeCode.SByte,
26         Byte        = TypeCode.Byte,
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
37         TimeSpan    = 17,
38         String      = TypeCode.String, // 18
39         Guid        = 19,
40
41         ByteArray   = 20,
42         CharArray   = 21,
43         Type        = 22,
44         DateTimeOffset = 23,
45         BigInteger  = 24,
46         Uri         = 25,
47
48         SqlBinary, // SqlTypes should remain at the end for IsSqlType checking
49         SqlBoolean,
50         SqlByte,
51         SqlBytes,
52         SqlChars,
53         SqlDateTime,
54         SqlDecimal,
55         SqlDouble,
56         SqlGuid,
57         SqlInt16,
58         SqlInt32,
59         SqlInt64,
60         SqlMoney,
61         SqlSingle,
62         SqlString,
63 //        SqlXml,
64     };
65
66     abstract internal class DataStorage {
67
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[] {
71             null,
72             typeof(Object),
73             typeof(DBNull),
74             typeof(Boolean),
75             typeof(Char),
76             typeof(SByte),
77             typeof(Byte),
78             typeof(Int16),
79             typeof(UInt16),
80             typeof(Int32),
81             typeof(UInt32),
82             typeof(Int64),
83             typeof(UInt64),
84             typeof(Single),
85             typeof(Double),
86             typeof(Decimal),
87             typeof(DateTime),
88             typeof(TimeSpan),
89             typeof(String),
90             typeof(Guid),
91
92             typeof(byte[]),
93             typeof(char[]),
94             typeof(Type),
95             typeof(DateTimeOffset),            
96             typeof(System.Numerics.BigInteger),
97             typeof(Uri),
98
99             typeof(SqlBinary),
100             typeof(SqlBoolean),
101             typeof(SqlByte),
102             typeof(SqlBytes),
103             typeof(SqlChars),
104             typeof(SqlDateTime),
105             typeof(SqlDecimal),
106             typeof(SqlDouble),
107             typeof(SqlGuid),
108             typeof(SqlInt16),
109             typeof(SqlInt32),
110             typeof(SqlInt64),
111             typeof(SqlMoney),
112             typeof(SqlSingle),
113             typeof(SqlString),
114 //            typeof(SqlXml),
115         };
116
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;
122
123         private readonly object DefaultValue;
124         internal readonly object NullValue;
125
126         internal readonly bool IsCloneable;
127         internal readonly bool IsCustomDefinedType;
128         internal readonly bool IsStringType;
129         internal readonly bool IsValueType;
130
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>>();
133
134         protected DataStorage(DataColumn column, Type type, object defaultValue, StorageType storageType)
135             : this(column, type, defaultValue, DBNull.Value, false, storageType) {
136         }
137
138         protected DataStorage(DataColumn column, Type type, object defaultValue, object nullValue, StorageType storageType)
139             : this(column, type, defaultValue, nullValue, false, storageType) {
140         }
141
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");
144             Column = column;
145             Table = column.Table;
146             DataType = type;
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);
154         }
155
156         internal DataSetDateTime DateTimeMode {
157             get {
158                 return Column.DateTimeMode;
159             }
160         }
161
162         internal IFormatProvider FormatProvider {
163             get {
164                 return Table.FormatProvider;
165             }
166         }
167
168         public virtual Object Aggregate(int[] recordNos, AggregateType kind) {
169             if (AggregateType.Count == kind) {
170                 return this.AggregateCount(recordNos);
171             }
172             return null;
173         }
174
175         public object AggregateCount(int[] recordNos) {
176             int count = 0;
177             for (int i = 0; i < recordNos.Length; i++) {
178                 if (!this.dbNullBits.Get(recordNos[i]))
179                     count++;
180             }
181             return count;
182         }
183
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) {
188                 if (recordNo1Null)
189                     return -1;
190                 else
191                     return 1;
192             }
193
194             return 0;
195         }
196
197         public abstract int Compare(int recordNo1, int recordNo2);
198
199         // only does comparision, expect value to be of the correct type
200         public abstract int CompareValueTo(int recordNo1, object value);
201
202         // only does conversion with support for reference null
203         public virtual object ConvertValue(object value) {
204             return value;
205         }
206
207         protected void CopyBits(int srcRecordNo, int dstRecordNo) {
208             this.dbNullBits.Set(dstRecordNo, this.dbNullBits.Get(srcRecordNo));
209         }
210
211         abstract public void Copy(int recordNo1, int recordNo2);
212
213         abstract public Object Get(int recordNo);
214
215         protected Object GetBits(int recordNo) {
216             if (this.dbNullBits.Get(recordNo)) {
217                 return NullValue;
218             }
219             return DefaultValue;
220         }
221
222         virtual public int GetStringLength(int record) {
223             System.Diagnostics.Debug.Assert(false, "not a String or SqlString column");
224             return Int32.MaxValue;
225         }
226
227         protected bool HasValue(int recordNo) {
228             return !this.dbNullBits.Get(recordNo);
229         }
230
231         public virtual bool IsNull(int recordNo) {
232             return this.dbNullBits.Get(recordNo);
233         }
234
235         // convert (may not support reference null) and store the value
236         abstract public void Set(int recordNo, Object value);
237
238         protected void SetNullBit(int recordNo, bool flag) {
239             this.dbNullBits.Set(recordNo, flag);
240         }
241
242         virtual public void SetCapacity(int capacity) {
243             if (null == this.dbNullBits) {
244                 this.dbNullBits = new BitArray(capacity);
245             }
246             else {
247                 this.dbNullBits.Length = capacity;
248             }
249         }
250
251         abstract public object ConvertXmlToObject(string s);
252         public virtual object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) {
253             return ConvertXmlToObject(xmlReader.Value);
254         }
255
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?
259         }
260
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);
266                 }
267                 else {
268                     return new ObjectStorage(column, dataType); // non-nullable non-primitives
269                 }
270             }
271
272             switch (typeCode) {
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);
293
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);
300
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);
317
318             default:
319                 System.Diagnostics.Debug.Assert(false, "shouldn't be here");
320                 goto case StorageType.Object;
321             }
322         }
323
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;
328                 }
329             }
330             TypeCode tcode = Type.GetTypeCode(dataType);
331             if (TypeCode.Object != tcode) { // enum -> Int64/Int32/Int16/Byte
332                 return (StorageType)tcode;
333             }
334             return StorageType.Empty;
335         }
336
337         internal static Type GetTypeStorage(StorageType storageType) {
338             return StorageClassType[(int)storageType];
339         }
340
341         internal static bool IsTypeCustomType(Type type) {
342             return IsTypeCustomType(GetStorageType(type));
343         }
344
345         internal static bool IsTypeCustomType(StorageType typeCode) {
346             return ((StorageType.Object == typeCode) || (StorageType.Empty == typeCode) || (StorageType.CharArray == typeCode));
347         }
348
349         internal static bool IsSqlType(StorageType storageType) {
350             return (StorageType.SqlBinary <= storageType);
351         }
352
353         public static bool IsSqlType(Type dataType) {
354             for (int i = (int)StorageType.SqlBinary; i < StorageClassType.Length; ++i) {
355                 if (dataType == StorageClassType[i]) {
356                     return true;
357                 }
358             }
359             return false;
360         }
361
362         private static bool DetermineIfValueType(StorageType typeCode, Type dataType) {
363             bool result;
364             switch (typeCode) {
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:
396                 result = true;
397                 break;
398
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:
406                 result = false;
407                 break;
408
409             default:
410                 result = dataType.IsValueType;
411                 break;
412             }
413             Debug.Assert(result == dataType.IsValueType, "typeCode mismatches dataType");
414             return result;
415         }
416
417         internal static void ImplementsInterfaces(
418                                     StorageType typeCode,
419                                     Type dataType,
420                                     out bool sqlType,
421                                     out bool nullable,
422                                     out bool xmlSerializable,
423                                     out bool changeTracking,
424                                     out bool revertibleChangeTracking)
425         {
426             Debug.Assert(typeCode == GetStorageType(dataType), "typeCode mismatches dataType");
427             if (IsSqlType(typeCode)) {
428                 sqlType = true;
429                 nullable = true;
430                 changeTracking = false;
431                 revertibleChangeTracking = false;
432                 xmlSerializable = true;
433             }
434             else if (StorageType.Empty != typeCode) {
435                 sqlType = false;
436                 nullable = false;
437                 changeTracking = false;
438                 revertibleChangeTracking = false;
439                 xmlSerializable = false;
440             }
441             else {
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);
444                 sqlType = false;
445                 nullable = interfaces.Item1;
446                 changeTracking = interfaces.Item2;
447                 revertibleChangeTracking = interfaces.Item3;
448                 xmlSerializable = interfaces.Item4;
449             }
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");
454         }
455
456         private static Tuple<bool, bool, bool, bool> InspectTypeForInterfaces(Type dataType) {
457             Debug.Assert(dataType != null, "Type should not be null");
458
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));
464         }
465
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<>)));
469         }
470
471         public static bool IsObjectNull(object value) {
472             return ((null == value) || (DBNull.Value == value) || IsObjectSqlNull(value));
473         }
474
475         public static bool IsObjectSqlNull(object value) {
476             INullable inullable = (value as INullable);
477             return ((null != inullable) && inullable.IsNull);
478         }
479
480         internal object GetEmptyStorageInternal(int recordCount) {
481             return GetEmptyStorage(recordCount);
482         }
483
484         internal void CopyValueInternal(int record, object store, BitArray nullbits, int storeIndex) {
485             CopyValue(record, store, nullbits, storeIndex);
486         }
487
488         internal void SetStorageInternal(object store, BitArray nullbits) {
489             SetStorage(store, nullbits);
490         }
491
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;
497         }
498
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>
503         /// <remarks>
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
507         /// </remarks>
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);
513                 }
514             }
515
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);
519             return dataType;
520         }
521
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)
527         {
528             Debug.Assert(null != type, "null type");
529             ObjectStorage.VerifyIDynamicMetaObjectProvider(type);
530             return type.AssemblyQualifiedName;
531         }
532     }
533 }