Merge pull request #4169 from evincarofautumn/fix-xmm-scanning-mac-x86
[mono.git] / mcs / class / referencesource / System.Data / System / Data / DataColumn.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataColumn.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data {
10     using System;
11     using System.Xml;
12     using System.Data.Common;
13     using System.ComponentModel;
14     using System.Diagnostics;
15     using System.Collections;
16     using System.Globalization;
17     using System.Data.SqlTypes;
18     using System.Xml.Serialization;
19     using System.Collections.Generic;
20     using System.Runtime.CompilerServices;
21
22     /// <devdoc>
23     ///    <para>
24     ///       Represents one column of data in a <see cref='System.Data.DataTable'/>.
25     ///    </para>
26     /// </devdoc>
27     [
28     ToolboxItem(false),
29     DesignTimeVisible(false),
30     DefaultProperty("ColumnName"),
31     Editor("Microsoft.VSDesigner.Data.Design.DataColumnEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
32     ]
33  public class DataColumn : MarshalByValueComponent {
34
35         // properties
36         private bool allowNull = true;
37         private string caption = null;
38         private string _columnName = null;
39         private Type dataType = null;
40         private StorageType _storageType;
41         internal object defaultValue = DBNull.Value;             // DefaultValue Converter
42         private DataSetDateTime _dateTimeMode = DataSetDateTime.UnspecifiedLocal;
43         private DataExpression expression = null;
44         private int maxLength = -1;
45         private int _ordinal = -1;
46         private bool readOnly = false;
47         internal Index sortIndex = null;
48         internal DataTable table = null;
49         private bool unique = false;
50         internal MappingType columnMapping = MappingType.Element;
51         internal int _hashCode;
52
53         internal int errors;
54         private bool isSqlType = false;
55         private bool implementsINullable = false;
56         private bool implementsIChangeTracking = false;
57         private bool implementsIRevertibleChangeTracking = false;
58         private bool implementsIXMLSerializable = false;
59
60         private bool defaultValueIsNull = true;
61
62         // list of columns whose expression consume values from this column
63         internal List<DataColumn> dependentColumns = null;
64
65         // collections
66         internal PropertyCollection extendedProperties = null;
67
68         // events
69         private PropertyChangedEventHandler onPropertyChangingDelegate = null;
70
71         // state
72         private DataStorage _storage;
73
74         /// <summary>represents current value to return, usage pattern is .get_Current then MoveAfter</summary>
75         private AutoIncrementValue autoInc;
76
77         //
78         // The _columnClass member is the class for the unfoliated virtual nodes in the XML.
79         //
80         internal string _columnUri = null;
81         private string _columnPrefix = "";
82         internal string encodedColumnName = null;
83
84         // 
85         internal string dttype = "";        // The type specified in dt:type attribute
86         internal SimpleType simpleType = null;
87
88         private static int _objectTypeCount; // Bid counter
89         private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
90
91         /// <devdoc>
92         ///    <para>
93         ///       Initializes a new instance of a <see cref='System.Data.DataColumn'/>
94         ///       class.
95         ///    </para>
96         /// </devdoc>
97         public DataColumn() : this(null, typeof(string), null, MappingType.Element) {
98         }
99
100         /// <devdoc>
101         ///    <para>
102         ///       Inititalizes a new instance of the <see cref='System.Data.DataColumn'/> class
103         ///       using the specified column name.
104         ///    </para>
105         /// </devdoc>
106         public DataColumn(string columnName) : this(columnName, typeof(string), null, MappingType.Element) {
107         }
108
109         /// <devdoc>
110         ///    <para>
111         ///       Inititalizes a new instance of the <see cref='System.Data.DataColumn'/> class
112         ///       using the specified column name and data type.
113         ///    </para>
114         /// </devdoc>
115         public DataColumn(string columnName, Type dataType) : this(columnName, dataType, null, MappingType.Element) {
116         }
117
118         /// <devdoc>
119         ///    <para>
120         ///       Initializes a new instance
121         ///       of the <see cref='System.Data.DataColumn'/> class
122         ///       using the specified name, data type, and expression.
123         ///    </para>
124         /// </devdoc>
125         public DataColumn(string columnName, Type dataType, string expr) : this(columnName, dataType, expr, MappingType.Element) {
126         }
127
128         /// <devdoc>
129         ///    <para>
130         ///       Initializes a new instance of the <see cref='System.Data.DataColumn'/> class
131         ///       using
132         ///       the specified name, data type, expression, and value that determines whether the
133         ///       column is an attribute.
134         ///    </para>
135         /// </devdoc>
136         public DataColumn(string columnName, Type dataType, string expr, MappingType type) {
137             GC.SuppressFinalize(this);
138             Bid.Trace("<ds.DataColumn.DataColumn|API> %d#, columnName='%ls', expr='%ls', type=%d{ds.MappingType}\n",
139                           ObjectID, columnName, expr, (int)type);
140
141             if (dataType == null) {
142                 throw ExceptionBuilder.ArgumentNull("dataType");
143             }
144
145             StorageType typeCode = DataStorage.GetStorageType(dataType);
146             if (DataStorage.ImplementsINullableValue(typeCode, dataType)) {
147                 throw ExceptionBuilder.ColumnTypeNotSupported();
148             }
149             _columnName = columnName ?? string.Empty;
150
151             SimpleType stype = SimpleType.CreateSimpleType(typeCode, dataType);
152             if (null != stype) {
153                 this.SimpleType = stype;
154             }
155             UpdateColumnType(dataType, typeCode);
156
157             if ((null != expr) && (0 < expr.Length)) {
158                 // @perfnote: its a performance hit to set Expression to the empty str when we know it will come out null
159                 this.Expression = expr;
160             }
161             this.columnMapping = type;
162         }
163
164
165         private void UpdateColumnType(Type type, StorageType typeCode) {
166             dataType = type;
167             _storageType = typeCode;
168             if (StorageType.DateTime != typeCode) { // revert _dateTimeMode back to default, when column type is changed
169                 _dateTimeMode = DataSetDateTime.UnspecifiedLocal;
170             }
171             DataStorage.ImplementsInterfaces(
172                                 typeCode, type,
173                                 out isSqlType,
174                                 out implementsINullable,
175                                 out implementsIXMLSerializable,
176                                 out implementsIChangeTracking,
177                                 out implementsIRevertibleChangeTracking);
178
179             if (!isSqlType && implementsINullable) {
180                 SqlUdtStorage.GetStaticNullForUdtType(type);
181             }
182         }
183
184         // PUBLIC PROPERTIES
185
186         /// <devdoc>
187         ///    <para>
188         ///       Gets or sets a value indicating whether null
189         ///       values are
190         ///       allowed in this column for rows belonging to the table.
191         ///    </para>
192         /// </devdoc>
193         [
194         ResCategoryAttribute(Res.DataCategory_Data),
195         DefaultValue(true),
196         ResDescriptionAttribute(Res.DataColumnAllowNullDescr)
197         ]
198         public bool AllowDBNull {
199             get {
200                 return allowNull;
201             }
202             set {
203                 IntPtr hscp;
204                 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_AllowDBNull|API> %d#, %d{bool}\n", ObjectID, value);
205                 try {
206                     if (allowNull != value) {
207                         if (table != null) {
208                             if (!value && table.EnforceConstraints)
209                                 CheckNotAllowNull();
210                         }
211                         this.allowNull = value;
212                     }
213                     // 
214                 }
215                 finally {
216                     Bid.ScopeLeave(ref hscp);
217                 }
218             }
219         }
220
221         /// <devdoc>
222         ///    <para>
223         ///       Gets or
224         ///       sets a value indicating whether the column automatically increments the value of the column for new
225         ///       rows added to the table.
226         ///    </para>
227         /// </devdoc>
228         [
229         ResCategoryAttribute(Res.DataCategory_Data),
230         RefreshProperties(RefreshProperties.All),
231         DefaultValue(false),
232         ResDescriptionAttribute(Res.DataColumnAutoIncrementDescr)
233         ]
234         public bool AutoIncrement {
235             get {
236                 return ((null != autoInc) && (autoInc.Auto));
237             }
238             set {
239                 Bid.Trace("<ds.DataColumn.set_AutoIncrement|API> %d#, %d{bool}\n", ObjectID, value);
240                 if (this.AutoIncrement != value) {
241                     if (value) {
242                         if (expression != null) {
243                             throw ExceptionBuilder.AutoIncrementAndExpression();
244                         }
245                         //                        if (defaultValue != null && defaultValue != DBNull.Value) {
246                         if (!DefaultValueIsNull) {
247                             throw ExceptionBuilder.AutoIncrementAndDefaultValue();
248                         }
249                         if (!IsAutoIncrementType(DataType)) {
250                             if (HasData) {
251                                 throw ExceptionBuilder.AutoIncrementCannotSetIfHasData(DataType.Name);
252                             }
253                             DataType = typeof(int);
254                         }
255                     }
256
257                     this.AutoInc.Auto = value;
258                 }
259             }
260         }
261
262         internal object AutoIncrementCurrent {
263             get { return ((null != this.autoInc) ? this.autoInc.Current : this.AutoIncrementSeed); }
264             set {
265                 if ((System.Numerics.BigInteger)this.AutoIncrementSeed != BigIntegerStorage.ConvertToBigInteger(value, this.FormatProvider)) {
266                     this.AutoInc.SetCurrent(value, this.FormatProvider);
267                 }
268             }
269         }
270
271         internal AutoIncrementValue AutoInc {
272             get {
273                 return (this.autoInc ?? (this.autoInc = ((this.DataType == typeof(System.Numerics.BigInteger)) 
274                                                          ? (AutoIncrementValue)new AutoIncrementBigInteger()
275                                                          : new AutoIncrementInt64())));
276             }
277         }
278
279
280         /// <devdoc>
281         ///    <para>
282         ///       Gets
283         ///       or sets the starting value for a column that has its
284         ///    <see cref='System.Data.DataColumn.AutoIncrement'/> property
285         ///       set to <see langword='true'/>
286         ///       .
287         ///    </para>
288         /// </devdoc>
289         [
290         ResCategoryAttribute(Res.DataCategory_Data),
291         DefaultValue((Int64)0),
292         ResDescriptionAttribute(Res.DataColumnAutoIncrementSeedDescr)
293         ]
294         public Int64 AutoIncrementSeed {
295             get {
296                 return ((null != this.autoInc) ? this.autoInc.Seed : 0L);
297             }
298             set {
299                 Bid.Trace("<ds.DataColumn.set_AutoIncrementSeed|API> %d#, %I64d\n", ObjectID, value);
300                 if (this.AutoIncrementSeed != value) {
301                     this.AutoInc.Seed = value;
302                 }
303             }
304         }
305
306         /// <devdoc>
307         ///    <para>
308         ///       Gets or sets the increment used by a column with its <see cref='System.Data.DataColumn.AutoIncrement'/>
309         ///       property set to <see langword='true'/>
310         ///       .
311         ///    </para>
312         /// </devdoc>
313         [
314         ResCategoryAttribute(Res.DataCategory_Data),
315         DefaultValue((Int64)1),
316         ResDescriptionAttribute(Res.DataColumnAutoIncrementStepDescr)
317         ]
318         public Int64 AutoIncrementStep {
319             get {
320                 return ((null != this.autoInc) ? this.autoInc.Step : 1L);
321             }
322             set {
323                 Bid.Trace("<ds.DataColumn.set_AutoIncrementStep|API> %d#, %I64d\n", ObjectID, value);
324                 if (this.AutoIncrementStep != value) {
325                     this.AutoInc.Step = value;
326                 }
327             }
328         }
329
330         /// <devdoc>
331         ///    <para>
332         ///       Gets or sets
333         ///       the caption for this column.
334         ///    </para>
335         /// </devdoc>
336         [
337         ResCategoryAttribute(Res.DataCategory_Data),
338         ResDescriptionAttribute(Res.DataColumnCaptionDescr)
339         ]
340         public string Caption {
341             get {
342                 return (caption != null) ? caption : _columnName;
343             }
344             set {
345                 if (value == null)
346                     value = "";
347
348                 if (caption == null || String.Compare(caption, value, true, Locale) != 0) {
349                     caption = value;
350                 }
351             }
352         }
353
354         /// <devdoc>
355         ///    <para>
356         ///       Resets the <see cref='System.Data.DataColumn.Caption'/> property to its previous value, or
357         ///       to <see langword='null'/> .
358         ///    </para>
359         /// </devdoc>
360         private void ResetCaption() {
361             if (caption != null) {
362                 caption = null;
363             }
364         }
365
366         /// <devdoc>
367         ///    <para>
368         ///       Gets a value indicating whether the <see cref='System.Data.DataColumn.Caption'/> has been explicitly set.
369         ///    </para>
370         /// </devdoc>
371         private bool ShouldSerializeCaption() {
372             return (caption != null);
373         }
374
375         /// <devdoc>
376         ///    <para>
377         ///       Gets or sets the name of the column within the <see cref='System.Data.DataColumnCollection'/>.
378         ///    </para>
379         /// </devdoc>
380         [
381         RefreshProperties(RefreshProperties.All),
382         ResCategoryAttribute(Res.DataCategory_Data),
383         DefaultValue(""),
384         ResDescriptionAttribute(Res.DataColumnColumnNameDescr)
385         ]
386         public string ColumnName {
387             get {
388                 return _columnName;
389             }
390             set {
391                 IntPtr hscp;
392                 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_ColumnName|API> %d#, '%ls'\n", ObjectID, value);
393                 try {
394                     if (value == null) {
395                         value = "";
396                     }
397
398                     if (String.Compare(_columnName, value, true, Locale) != 0) {
399                         if (table != null) {
400                             if (value.Length == 0)
401                                 throw ExceptionBuilder.ColumnNameRequired();
402
403                             table.Columns.RegisterColumnName(value, this);
404                             if (_columnName.Length != 0)
405                                 table.Columns.UnregisterName(_columnName);
406                         }
407
408                         RaisePropertyChanging("ColumnName");
409                         _columnName = value;
410                         encodedColumnName = null;
411                         if (table != null) {
412                             table.Columns.OnColumnPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this));
413                         }
414                     }
415                     else if (_columnName != value) {
416                         RaisePropertyChanging("ColumnName");
417                         _columnName = value;
418                         encodedColumnName = null;
419                         if (table != null) {
420                             table.Columns.OnColumnPropertyChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, this));
421                         }
422                     }
423                 }
424                 finally {
425                     Bid.ScopeLeave(ref hscp);
426                 }
427             }
428         }
429
430         internal string EncodedColumnName {
431             get {
432                 if (this.encodedColumnName == null) {
433                     this.encodedColumnName = XmlConvert.EncodeLocalName(this.ColumnName);
434                 }
435                 Debug.Assert(this.encodedColumnName != null && this.encodedColumnName.Length != 0);
436                 return this.encodedColumnName;
437             }
438         }
439
440         internal IFormatProvider FormatProvider {
441             get {
442                 // used for formating/parsing not comparing
443                 return ((null != table) ? table.FormatProvider : CultureInfo.CurrentCulture);
444             }
445         }
446
447         internal CultureInfo Locale {
448             get {
449                 // used for comparing not formating/parsing
450                 return ((null != table) ? table.Locale : CultureInfo.CurrentCulture);
451             }
452         }
453
454         internal int ObjectID {
455             get {
456                 return _objectID;
457             }
458         }
459
460         [
461         ResCategoryAttribute(Res.DataCategory_Data),
462         DefaultValue(""),
463         ResDescriptionAttribute(Res.DataColumnPrefixDescr)
464         ]
465         public string Prefix {
466             get { return _columnPrefix; }
467             set {
468                 if (value == null)
469                     value = "";
470                 Bid.Trace("<ds.DataColumn.set_Prefix|API> %d#, '%ls'\n", ObjectID, value);
471
472                 if ((XmlConvert.DecodeName(value) == value) && (XmlConvert.EncodeName(value) != value))
473                     throw ExceptionBuilder.InvalidPrefix(value);
474
475                 _columnPrefix = value;
476
477             }
478         }
479
480         // Return the field value as a string. If the field value is NULL, then NULL is return.
481         // If the column type is string and it's value is empty, then the empty string is returned.
482         // If the column type is not string, or the column type is string and the value is not empty string, then a non-empty string is returned
483         // This method does not throw any formatting exceptions, since we can always format the field value to a string.
484         internal string GetColumnValueAsString(DataRow row, DataRowVersion version) {
485
486             object objValue = this[row.GetRecordFromVersion(version)];
487
488             if (DataStorage.IsObjectNull(objValue)) {
489                 return null;
490             }
491
492             string value = ConvertObjectToXml(objValue);
493             Debug.Assert(value != null);
494
495             return value;
496         }
497
498         /// <devdoc>
499         /// Whether this column computes values.
500         /// </devdoc>
501         internal bool Computed {
502             get {
503                 return this.expression != null;
504             }
505         }
506
507         /// <devdoc>
508         /// The internal expression object that computes the values.
509         /// </devdoc>
510         internal DataExpression DataExpression {
511             get {
512                 return this.expression;
513             }
514         }
515
516         /// <devdoc>
517         ///    <para>
518         ///       The type
519         ///       of data stored in thecolumn.
520         ///    </para>
521         /// </devdoc>
522         [
523         ResCategoryAttribute(Res.DataCategory_Data),
524         DefaultValue(typeof(string)),
525         RefreshProperties(RefreshProperties.All),
526         TypeConverter(typeof(ColumnTypeConverter)),
527         ResDescriptionAttribute(Res.DataColumnDataTypeDescr)
528         ]
529         public Type DataType {
530             get {
531                 return dataType;
532             }
533             set {
534                 if (dataType != value) {
535                     if (HasData) {
536                         throw ExceptionBuilder.CantChangeDataType();
537                     }
538                     if (value == null) {
539                         throw ExceptionBuilder.NullDataType();
540                     }
541                     StorageType typeCode = DataStorage.GetStorageType(value);
542                     if (DataStorage.ImplementsINullableValue(typeCode, value)) {
543                         throw ExceptionBuilder.ColumnTypeNotSupported();
544                     }
545                     if (table != null && IsInRelation()) {
546                         throw ExceptionBuilder.ColumnsTypeMismatch();
547                     }
548                     if (typeCode == StorageType.BigInteger && this.expression != null)
549                     {
550                         throw ExprException.UnsupportedDataType(value);
551                     }
552
553                     // If the DefualtValue is different from the Column DataType, we will coerce the value to the DataType
554                     if (!DefaultValueIsNull) {
555                         try {
556                             if (this.defaultValue is System.Numerics.BigInteger) {
557                                 this.defaultValue = BigIntegerStorage.ConvertFromBigInteger((System.Numerics.BigInteger)this.defaultValue, value, this.FormatProvider);
558                             }
559                             else if (typeof(System.Numerics.BigInteger) == value) {
560                                 this.defaultValue = BigIntegerStorage.ConvertToBigInteger(this.defaultValue, this.FormatProvider);
561                             }
562                             else if (typeof(string) == value) { // since string types can be null in value! DO NOT REMOVE THIS 
563                                 defaultValue = DefaultValue.ToString();
564                             }
565                             else if (typeof(SqlString) == value) { // since string types can be null in value! DO NOT REMOVE THIS 
566                                 defaultValue = SqlConvert.ConvertToSqlString(DefaultValue);
567                             }
568                             else if (typeof(object) != value) {
569                                 DefaultValue = SqlConvert.ChangeTypeForDefaultValue(DefaultValue, value, FormatProvider);
570                             }
571                         }
572                         catch (InvalidCastException ex) {
573                             throw ExceptionBuilder.DefaultValueDataType(ColumnName, DefaultValue.GetType(), value, ex);
574                         }
575                         catch (FormatException ex) {
576                             throw ExceptionBuilder.DefaultValueDataType(ColumnName, DefaultValue.GetType(), value, ex);
577                         }
578                     }
579
580                     if (this.ColumnMapping == MappingType.SimpleContent)
581                         if (value == typeof(Char))
582                             throw ExceptionBuilder.CannotSetSimpleContentType(ColumnName, value);
583
584                     SimpleType = SimpleType.CreateSimpleType(typeCode, value);
585                     if (StorageType.String == typeCode) {
586                         maxLength = -1;
587                     }
588                     UpdateColumnType(value, typeCode);
589                     XmlDataType = null;
590
591                     if (AutoIncrement) {
592                         if (!IsAutoIncrementType(value)) {
593                             AutoIncrement = false;
594                         }
595
596                         if (null != this.autoInc) {
597                             // if you already have data you can't change the data type
598                             // if you don't have data - you wouldn't have incremented AutoIncrementCurrent.
599                             AutoIncrementValue inc = this.autoInc;
600                             this.autoInc = null;
601                             this.AutoInc.Auto = inc.Auto; // recreate with correct datatype
602                             this.AutoInc.Seed = inc.Seed;
603                             this.AutoInc.Step = inc.Step;
604                             if (this.autoInc.DataType == inc.DataType) {
605                                 this.autoInc.Current = inc.Current;
606                             }
607                             else if (inc.DataType == typeof(Int64)) {
608                                 this.AutoInc.Current = (System.Numerics.BigInteger)(long)inc.Current;
609                             }
610                             else {
611                                 this.AutoInc.Current = checked((long)(System.Numerics.BigInteger)inc.Current);
612                             }
613                         }
614                     }
615                 }
616             }
617         }
618
619         [
620         ResCategoryAttribute(Res.DataCategory_Data),
621         DefaultValue(DataSetDateTime.UnspecifiedLocal),
622         RefreshProperties(RefreshProperties.All),
623         ResDescriptionAttribute(Res.DataColumnDateTimeModeDescr)
624         ]
625         public DataSetDateTime DateTimeMode {
626             get {
627                 return _dateTimeMode;
628             }
629             set {
630                 if (_dateTimeMode != value) {
631                     if (DataType != typeof(DateTime) && value != DataSetDateTime.UnspecifiedLocal) { //Check for column being DateTime. If the column is not DateTime make sure the value that is being is only the default[UnspecifiedLocal].
632                         throw ExceptionBuilder.CannotSetDateTimeModeForNonDateTimeColumns();
633                     }
634                     switch (value) {
635                     case DataSetDateTime.Utc:
636                     case DataSetDateTime.Local:
637                         if (HasData) {
638                             throw ExceptionBuilder.CantChangeDateTimeMode(_dateTimeMode, value);
639                         }
640                         break;
641                     case DataSetDateTime.Unspecified:
642                     case DataSetDateTime.UnspecifiedLocal:
643                         if (_dateTimeMode == DataSetDateTime.Unspecified || _dateTimeMode == DataSetDateTime.UnspecifiedLocal) {
644                             break;
645                         }
646                         if (HasData) {
647                             throw ExceptionBuilder.CantChangeDateTimeMode(_dateTimeMode, value);
648                         }
649                         break;
650                     default:
651                         throw ExceptionBuilder.InvalidDateTimeMode(value);
652                     }
653                     _dateTimeMode = value;
654                 }
655             }
656         }
657
658         /// <devdoc>
659         ///    <para>Gets or sets the default value for the
660         ///       column when creating new rows.</para>
661         /// </devdoc>
662         [
663         ResCategoryAttribute(Res.DataCategory_Data),
664         ResDescriptionAttribute(Res.DataColumnDefaultValueDescr),
665         TypeConverter(typeof(DefaultValueTypeConverter))
666         ]
667         public object DefaultValue {
668             get {
669                 Debug.Assert(defaultValue != null, "It should not have been set to null.");
670                 if (defaultValue == DBNull.Value && this.implementsINullable) { // for perf I dont access property
671                     if (_storage != null)
672                         defaultValue = _storage._nullValue;
673                     else if (this.isSqlType)
674                             defaultValue = SqlConvert.ChangeTypeForDefaultValue(defaultValue, this.dataType, FormatProvider);
675                     else if (this.implementsINullable) {
676                         System.Reflection.PropertyInfo propInfo = this.dataType.GetProperty("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
677                         if (propInfo != null)
678                             defaultValue = propInfo.GetValue(null, null);
679                     }
680                 }
681
682                 return defaultValue;
683             }
684             set {
685                 Bid.Trace("<ds.DataColumn.set_DefaultValue|API> %d#\n", ObjectID);
686                 if (defaultValue == null || !DefaultValue.Equals(value)) {
687                     if (AutoIncrement) {
688                         throw ExceptionBuilder.DefaultValueAndAutoIncrement();
689                     }
690
691                     object newDefaultValue = (value == null) ? DBNull.Value : value;
692                     if (newDefaultValue != DBNull.Value && DataType != typeof(Object)) {
693                         // If the DefualtValue is different from the Column DataType, we will coerce the value to the DataType
694                         try {
695                             newDefaultValue = SqlConvert.ChangeTypeForDefaultValue(newDefaultValue, DataType, FormatProvider);
696                         }
697                         catch (InvalidCastException ex) {
698                             throw ExceptionBuilder.DefaultValueColumnDataType(ColumnName, newDefaultValue.GetType(), DataType, ex);
699                         }
700                     }
701                     defaultValue = newDefaultValue;
702                     // SQL BU Defect Tracking 401640:  should not assign any value until conversion is successful.
703                     defaultValueIsNull = ((newDefaultValue == DBNull.Value) || (this.ImplementsINullable && DataStorage.IsObjectSqlNull(newDefaultValue))) ? true : false;
704                 }
705             }
706         }
707
708         internal bool DefaultValueIsNull {
709             get {
710                 return defaultValueIsNull;
711             }
712         }
713
714         internal void BindExpression() {
715             this.DataExpression.Bind(this.table);
716         }
717
718         /// <devdoc>
719         ///    <para>Gets
720         ///       or sets the expresssion used to either filter rows, calculate the column's
721         ///       value, or create an aggregate column.</para>
722         /// </devdoc>
723         [
724         ResCategoryAttribute(Res.DataCategory_Data),
725         RefreshProperties(RefreshProperties.All),
726         DefaultValue(""),
727         ResDescriptionAttribute(Res.DataColumnExpressionDescr)
728         ]
729         public string Expression {
730             get {
731                 return (this.expression == null ? "" : this.expression.Expression);
732             }
733             set {
734                 IntPtr hscp;
735                 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_Expression|API> %d#, '%ls'\n", ObjectID, value);
736
737                 if (value == null) {
738                     value = "";
739                 }
740
741                 try {
742                     DataExpression newExpression = null;
743                     if (value.Length > 0) {
744                         DataExpression testExpression = new DataExpression(this.table, value, this.dataType);
745                         if (testExpression.HasValue) {
746                             newExpression = testExpression;
747                         }
748                     }
749
750                     if (expression == null && newExpression != null) {
751                         if (AutoIncrement || Unique) {
752                             throw ExceptionBuilder.ExpressionAndUnique();
753                         }
754
755                         // We need to make sure the column is not involved in any Constriants
756                         if (table != null) {
757                             for (int i = 0; i < table.Constraints.Count; i++) {
758                                 if (table.Constraints[i].ContainsColumn(this)) {
759                                     throw ExceptionBuilder.ExpressionAndConstraint(this, table.Constraints[i]);
760                                 }
761                             }
762                         }
763
764                         bool oldReadOnly = ReadOnly;
765                         try {
766                             ReadOnly = true;
767                         }
768                         catch (ReadOnlyException e) {
769                             ExceptionBuilder.TraceExceptionForCapture(e);
770                             ReadOnly = oldReadOnly;
771                             throw ExceptionBuilder.ExpressionAndReadOnly();
772                         }
773                     }
774
775                     // re-calculate the evaluation queue
776                     if (this.table != null) {
777                         if (newExpression != null && newExpression.DependsOn(this)) {
778                             throw ExceptionBuilder.ExpressionCircular();
779                         }
780                         HandleDependentColumnList(expression, newExpression);
781                         //hold onto oldExpression in case of error applying new Expression.
782                         DataExpression oldExpression = this.expression;
783                         this.expression = newExpression;
784
785                         // because the column is attached to a table we need to re-calc values
786                         try {
787                             if (newExpression == null) {
788                                 for (int i = 0; i < table.RecordCapacity; i++) {
789                                     InitializeRecord(i);
790                                 }
791                             }
792                             else {
793                                 this.table.EvaluateExpressions(this);
794                             }
795                             // SQLBU 501916: DataTable internal index is corrupted:'5'
796                             this.table.ResetInternalIndexes(this);
797                             this.table.EvaluateDependentExpressions(this);
798                         }
799                         catch (Exception e1) {
800                             // 
801                             if (!ADP.IsCatchableExceptionType(e1)) {
802                                 throw;
803                             }
804                             ExceptionBuilder.TraceExceptionForCapture(e1);
805                             try {
806                                 // in the case of error we need to set the column expression to the old value
807                                 this.expression = oldExpression;
808                                 HandleDependentColumnList(newExpression, expression);
809                                 if (oldExpression == null) {
810                                     for (int i = 0; i < table.RecordCapacity; i++) {
811                                         InitializeRecord(i);
812                                     }
813                                 }
814                                 else {
815                                     this.table.EvaluateExpressions(this);
816                                 }
817                                 this.table.ResetInternalIndexes(this);
818                                 this.table.EvaluateDependentExpressions(this);
819                             }
820                             catch (Exception e2) {
821                                 // 
822                                 if (!ADP.IsCatchableExceptionType(e2)) {
823                                     throw;
824                                 }
825                                 ExceptionBuilder.TraceExceptionWithoutRethrow(e2);
826                             }
827                             throw;
828                         }
829                     }
830                     else {
831                         //if column is not attached to a table, just set.
832                         this.expression = newExpression;
833                     }
834                 }
835                 finally {
836                     Bid.ScopeLeave(ref hscp);
837                 }
838             }
839         }
840
841         /// <devdoc>
842         ///    <para>Gets the collection of custom user information.</para>
843         /// </devdoc>
844         [
845         ResCategoryAttribute(Res.DataCategory_Data),
846         Browsable(false),
847         ResDescriptionAttribute(Res.ExtendedPropertiesDescr)
848         ]
849         public PropertyCollection ExtendedProperties {
850             get {
851                 if (extendedProperties == null) {
852                     extendedProperties = new PropertyCollection();
853                 }
854                 return extendedProperties;
855             }
856         }
857
858         /// <devdoc>
859         /// Indicates whether this column is now storing data.
860         /// </devdoc>
861         internal bool HasData {
862             get {
863                 return (_storage != null);
864             }
865         }
866
867         internal bool ImplementsINullable {
868             get {
869                 return implementsINullable;
870             }
871         }
872
873         internal bool ImplementsIChangeTracking {
874             get {
875                 return implementsIChangeTracking;
876             }
877         }
878
879         internal bool ImplementsIRevertibleChangeTracking {
880             get {
881                 return implementsIRevertibleChangeTracking;
882             }
883         }
884
885         internal bool IsCloneable {
886             get {
887                 Debug.Assert(null != _storage, "no storage");
888                 return _storage._isCloneable;
889             }
890         }
891
892         internal bool IsStringType {
893             get {
894                 Debug.Assert(null != _storage, "no storage");
895                 return _storage._isStringType;
896             }
897         }
898
899         internal bool IsValueType {
900             get {
901                 Debug.Assert(null != _storage, "no storage");
902                 return _storage._isValueType;
903             }
904         }
905
906         internal bool IsSqlType {
907             get {
908                 return isSqlType;
909             }
910         }
911
912         private void SetMaxLengthSimpleType() {
913             if (this.simpleType != null) {
914                 Debug.Assert(this.simpleType.CanHaveMaxLength(), "expected simpleType to be string");
915
916                 this.simpleType.MaxLength = maxLength;
917                 // check if we reset the simpleType back to plain string
918                 if (this.simpleType.IsPlainString()) {
919                     this.simpleType = null;
920                 }
921                 else {
922                     // Named Simple Type's Name should not be null
923                     if (this.simpleType.Name != null && this.dttype != null) {
924                         // if MaxLength is changed, we need to make  namedsimpletype annonymous simpletype
925                         this.simpleType.ConvertToAnnonymousSimpleType();
926                         this.dttype = null;
927                     }
928                 }
929             }
930             else if (-1 < maxLength) {
931                 this.SimpleType = SimpleType.CreateLimitedStringType(maxLength);
932             }
933         }
934         [
935         ResCategoryAttribute(Res.DataCategory_Data),
936         ResDescriptionAttribute(Res.DataColumnMaxLengthDescr),
937         DefaultValue(-1)
938         ]
939         public int MaxLength {
940             get {
941                 return maxLength;
942             }
943             set {
944                 IntPtr hscp;
945                 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_MaxLength|API> %d#, %d\n", ObjectID, value);
946
947                 try {
948                     if (maxLength != value) {
949                         if (this.ColumnMapping == MappingType.SimpleContent) {
950                             throw ExceptionBuilder.CannotSetMaxLength2(this);
951                         }
952                         if ((DataType != typeof(string)) && (DataType != typeof(SqlString))) {
953                             throw ExceptionBuilder.HasToBeStringType(this);
954                         }
955                         int oldValue = maxLength;
956                         maxLength = Math.Max(value, -1);
957
958                         if (((oldValue < 0) || (value < oldValue)) && (null != table) && table.EnforceConstraints) {
959                             if (!CheckMaxLength()) {
960                                 maxLength = oldValue;
961                                 throw ExceptionBuilder.CannotSetMaxLength(this, value);
962                             }
963                         }
964                         SetMaxLengthSimpleType();
965                     }
966                 }
967                 finally {
968                     Bid.ScopeLeave(ref hscp);
969                 }
970             }
971         }
972
973         [
974         ResCategoryAttribute(Res.DataCategory_Data),
975         ResDescriptionAttribute(Res.DataColumnNamespaceDescr)
976         ]
977         public string Namespace {
978             get {
979                 if (_columnUri == null) {
980                     if (Table != null && columnMapping != MappingType.Attribute) {
981                         return Table.Namespace;
982                     }
983                     return "";
984                 }
985                 return _columnUri;
986             }
987             set {
988                 Bid.Trace("<ds.DataColumn.set_Namespace|API> %d#, '%ls'\n", ObjectID, value);
989
990                 if (_columnUri != value) {
991                     if (columnMapping != MappingType.SimpleContent) {
992                         RaisePropertyChanging("Namespace");
993                         _columnUri = value;
994                     }
995                     else if (value != this.Namespace) {
996                         throw ExceptionBuilder.CannotChangeNamespace(this.ColumnName);
997                     }
998                 }
999             }
1000         }
1001
1002         private bool ShouldSerializeNamespace() {
1003             return (_columnUri != null);
1004         }
1005
1006         private void ResetNamespace() {
1007             this.Namespace = null;
1008         }
1009
1010         /// <devdoc>
1011         ///    <para>
1012         ///       Gets the position of the column in the <see cref='System.Data.DataColumnCollection'/>
1013         ///       collection.
1014         ///    </para>
1015         /// </devdoc>
1016         [
1017         ResCategoryAttribute(Res.DataCategory_Data),
1018         Browsable(false),
1019         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
1020         ResDescriptionAttribute(Res.DataColumnOrdinalDescr)
1021         ]
1022         public int Ordinal {
1023             get {
1024                 return _ordinal;
1025             }
1026         }
1027
1028         public void SetOrdinal(int ordinal) {
1029             if (_ordinal == -1) {
1030                 throw ExceptionBuilder.ColumnNotInAnyTable();
1031             }
1032
1033             if (this._ordinal != ordinal) {
1034                 table.Columns.MoveTo(this, ordinal);
1035             }
1036         }
1037
1038         internal void SetOrdinalInternal(int ordinal) {
1039             // 
1040             if (this._ordinal != ordinal) {
1041                 if (Unique && this._ordinal != -1 && ordinal == -1) {
1042                     UniqueConstraint key = table.Constraints.FindKeyConstraint(this);
1043                     if (key != null)
1044                         table.Constraints.Remove(key);
1045                 }
1046                 // SQLBU 429176: remove the sortIndex when DataColumn is removed
1047                 if ((null != sortIndex) && (-1 == ordinal)) {
1048                     Debug.Assert(2 <= sortIndex.RefCount, "bad sortIndex refcount");
1049                     sortIndex.RemoveRef();
1050                     sortIndex.RemoveRef(); // second should remove it from index collection
1051                     sortIndex = null;
1052                 }
1053                 int originalOrdinal = this._ordinal;
1054                 this._ordinal = ordinal;
1055                 if (originalOrdinal == -1 && this._ordinal != -1) {
1056                     if (Unique) {
1057                         UniqueConstraint key = new UniqueConstraint(this);
1058                         table.Constraints.Add(key);
1059                     }
1060                 }
1061             }
1062         }
1063
1064         /// <devdoc>
1065         ///    <para>
1066         ///       Gets or sets a value
1067         ///       indicating whether the column allows changes once a row has been added to the table.
1068         ///    </para>
1069         /// </devdoc>
1070         [
1071         ResCategoryAttribute(Res.DataCategory_Data),
1072         DefaultValue(false),
1073         ResDescriptionAttribute(Res.DataColumnReadOnlyDescr)
1074         ]
1075         public bool ReadOnly {
1076             get {
1077                 return readOnly;
1078             }
1079             set {
1080                 Bid.Trace("<ds.DataColumn.set_ReadOnly|API> %d#, %d{bool}\n", ObjectID, value);
1081                 if (readOnly != value) {
1082                     if (!value && expression != null) {
1083                         throw ExceptionBuilder.ReadOnlyAndExpression();
1084                     }
1085                     this.readOnly = value;
1086                 }
1087             }
1088         }
1089
1090         [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
1091         private Index SortIndex {
1092             get {
1093                 if (sortIndex == null) {
1094                     IndexField[] indexDesc = new IndexField[] { new IndexField(this, false) };
1095                     sortIndex = table.GetIndex(indexDesc, DataViewRowState.CurrentRows, (IFilter)null);
1096                     sortIndex.AddRef();
1097                 }
1098                 return sortIndex;
1099             }
1100         }
1101
1102         /// <devdoc>
1103         ///    <para>
1104         ///       Gets the <see cref='System.Data.DataTable'/> to which the column belongs to.
1105         ///    </para>
1106         /// </devdoc>
1107         [
1108         ResCategoryAttribute(Res.DataCategory_Data),
1109         Browsable(false),
1110         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
1111         ResDescriptionAttribute(Res.DataColumnDataTableDescr)
1112         ]
1113         public DataTable Table {
1114             get {
1115                 return table;
1116             }
1117         }
1118
1119         /// <devdoc>
1120         /// Internal mechanism for changing the table pointer.
1121         /// </devdoc>
1122         internal void SetTable(DataTable table) {
1123             if (this.table != table) {
1124                 if (this.Computed)
1125                     if ((table == null) ||
1126                         (!table.fInitInProgress && ((table.DataSet == null) || (!table.DataSet.fIsSchemaLoading && !table.DataSet.fInitInProgress)))) {
1127                         // We need to re-bind all expression columns.
1128                         this.DataExpression.Bind(table);
1129                     }
1130
1131                 if (Unique && this.table != null) {
1132                     UniqueConstraint constraint = table.Constraints.FindKeyConstraint(this);
1133                     if (constraint != null)
1134                         table.Constraints.CanRemove(constraint, true);
1135                 }
1136                 this.table = table;
1137                 _storage = null; // empty out storage for reuse.
1138             }
1139         }
1140
1141         private DataRow GetDataRow(int index) {
1142             return table.recordManager[index];
1143         }
1144
1145         /// <devdoc>
1146         /// This is how data is pushed in and out of the column.
1147         /// </devdoc>
1148         internal object this[int record] {
1149             get {
1150                 table.recordManager.VerifyRecord(record);
1151                 Debug.Assert(null != _storage, "null storage");
1152                 return _storage.Get(record);
1153             }
1154             set {
1155                 try {
1156                     table.recordManager.VerifyRecord(record);
1157                     Debug.Assert(null != _storage, "no storage");
1158                     Debug.Assert(null != value, "setting null, expecting dbnull");
1159                     _storage.Set(record, value);
1160                     Debug.Assert(null != this.table, "storage with no DataTable on column");
1161                 }
1162                 catch (Exception e) {
1163                     ExceptionBuilder.TraceExceptionForCapture(e);
1164                     throw ExceptionBuilder.SetFailed(value, this, DataType, e);
1165                 }
1166
1167                 if (AutoIncrement) {
1168                     if (!_storage.IsNull(record)) {
1169                         this.AutoInc.SetCurrentAndIncrement(_storage.Get(record));
1170                     }
1171                 }
1172                 if (Computed) {// if and only if it is Expression column, we will cache LastChangedColumn, otherwise DO NOT
1173                     DataRow dr = GetDataRow(record);
1174                     if (dr != null) {
1175                         // at initialization time (datatable.NewRow(), we would fill the storage with default value, but at that time we wont have datarow)
1176                         dr.LastChangedColumn = this;
1177                     }
1178                 }
1179             }
1180         }
1181
1182         internal void InitializeRecord(int record) {
1183             Debug.Assert(null != _storage, "no storage");
1184             _storage.Set(record, DefaultValue);
1185         }
1186
1187         internal void SetValue(int record, object value) { // just silently set the value
1188             try {
1189                 Debug.Assert(null != value, "setting null, expecting dbnull");
1190                 Debug.Assert(null != this.table, "storage with no DataTable on column");
1191                 Debug.Assert(null != _storage, "no storage");
1192                 _storage.Set(record, value);
1193             }
1194             catch (Exception e) {
1195                 ExceptionBuilder.TraceExceptionForCapture(e);
1196                 throw ExceptionBuilder.SetFailed(value, this, DataType, e);
1197             }
1198
1199             DataRow dr = GetDataRow(record);
1200             if (dr != null) {  // at initialization time (datatable.NewRow(), we would fill the storage with default value, but at that time we wont have datarow)
1201                 dr.LastChangedColumn = this;
1202             }
1203         }
1204
1205         internal void FreeRecord(int record) {
1206             Debug.Assert(null != _storage, "no storage");
1207             _storage.Set(record, _storage._nullValue);
1208         }
1209
1210         /// <devdoc>
1211         ///    <para>
1212         ///       Gets or sets a value indicating whether the values in each row of the column must be unique.
1213         ///    </para>
1214         /// </devdoc>
1215         [
1216         ResCategoryAttribute(Res.DataCategory_Data),
1217         DefaultValue(false),
1218         ResDescriptionAttribute(Res.DataColumnUniqueDescr),
1219         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
1220         ]
1221         public bool Unique {
1222             get {
1223                 return unique;
1224             }
1225             set {
1226                 IntPtr hscp;
1227                 Bid.ScopeEnter(out hscp, "<ds.DataColumn.set_Unique|API> %d#, %d{bool}\n", ObjectID, value);
1228                 try {
1229                     if (unique != value) {
1230                         if (value && expression != null) {
1231                             throw ExceptionBuilder.UniqueAndExpression();
1232                         }
1233                         UniqueConstraint oldConstraint = null;
1234                         if (table != null) {
1235                             if (value)
1236                                 CheckUnique();
1237                             else {
1238                                 for (System.Collections.IEnumerator e = Table.Constraints.GetEnumerator(); e.MoveNext(); ) {
1239                                     UniqueConstraint o = (e.Current as UniqueConstraint);
1240                                     if ((null != o) && (o.ColumnsReference.Length == 1) && (o.ColumnsReference[0] == this))
1241                                         oldConstraint = o;
1242                                 }
1243                                 Debug.Assert(oldConstraint != null, "Should have found a column to remove from the collection.");
1244                                 table.Constraints.CanRemove(oldConstraint, true);
1245                             }
1246                         }
1247
1248                         this.unique = value;
1249
1250                         if (table != null) {
1251                             if (value) {
1252                                 // This should not fail due to a duplicate constraint. unique would have
1253                                 // already been true if there was an existed UniqueConstraint for this column
1254
1255                                 UniqueConstraint constraint = new UniqueConstraint(this);
1256                                 Debug.Assert(table.Constraints.FindKeyConstraint(this) == null, "Should not be a duplication constraint in collection");
1257                                 table.Constraints.Add(constraint);
1258                             }
1259                             else
1260                             {
1261                                 table.Constraints.Remove(oldConstraint);
1262                                 // 
1263                             }
1264                         }
1265                     }
1266                 }
1267                 finally {
1268                     Bid.ScopeLeave(ref hscp);
1269                 }
1270             }
1271         }
1272
1273
1274         // FxCop Rule; getter not used!  WebData 101301; so changing from Property to method
1275         internal void InternalUnique(bool value) {
1276             this.unique = value;
1277         }
1278
1279         internal string XmlDataType {
1280             get {
1281                 return dttype;
1282             }
1283             set {
1284                 dttype = value;
1285             }
1286         }
1287
1288         internal SimpleType SimpleType {
1289             get {
1290                 return simpleType;
1291             }
1292             set {
1293                 simpleType = value;
1294                 // there is a change, since we are supporting hierarchy(bacause of Names Simple Type) old check (just one leel base check) is wrong
1295                 if (value != null && value.CanHaveMaxLength())
1296                     maxLength = simpleType.MaxLength;// this is temp solution, since we dont let simple content to have
1297                 //maxlength set but for simple type we want to set it, after coming to decision about it , we should
1298                 // use MaxLength property
1299             }
1300         }
1301
1302         /// <devdoc>
1303         /// <para>Gets the <see cref='System.Data.MappingType'/> of the column.</para>
1304         /// </devdoc>
1305         [
1306         DefaultValue(MappingType.Element),
1307         ResDescriptionAttribute(Res.DataColumnMappingDescr)
1308         ]
1309         public virtual MappingType ColumnMapping {
1310             get {
1311                 return columnMapping;
1312             }
1313             set {
1314                 Bid.Trace("<ds.DataColumn.set_ColumnMapping|API> %d#, %d{ds.MappingType}\n", ObjectID, (int)value);
1315                 if (value != columnMapping) {
1316
1317                     if (value == MappingType.SimpleContent && table != null) {
1318                         int threshold = 0;
1319                         if (columnMapping == MappingType.Element)
1320                             threshold = 1;
1321                         if (this.dataType == typeof(Char))
1322                             throw ExceptionBuilder.CannotSetSimpleContent(ColumnName, this.dataType);
1323
1324                         if (table.XmlText != null && table.XmlText != this)
1325                             throw ExceptionBuilder.CannotAddColumn3();
1326                         if (table.ElementColumnCount > threshold)
1327                             throw ExceptionBuilder.CannotAddColumn4(this.ColumnName);
1328                     }
1329
1330                     RaisePropertyChanging("ColumnMapping");
1331
1332                     if (table != null) {
1333                         if (columnMapping == MappingType.SimpleContent)
1334                             table.xmlText = null;
1335
1336                         if (value == MappingType.Element)
1337                             table.ElementColumnCount++;
1338                         else if (columnMapping == MappingType.Element)
1339                             table.ElementColumnCount--;
1340                     }
1341
1342                     columnMapping = value;
1343                     if (value == MappingType.SimpleContent) {
1344                         _columnUri = null;
1345                         if (table != null) {
1346                             table.XmlText = this;
1347                         }
1348                         this.SimpleType = null;
1349                     }
1350                 }
1351             }
1352         }
1353
1354         internal event PropertyChangedEventHandler PropertyChanging {
1355             add {
1356                 onPropertyChangingDelegate += value;
1357             }
1358             remove {
1359                 onPropertyChangingDelegate -= value;
1360             }
1361         }
1362
1363         internal void CheckColumnConstraint(DataRow row, DataRowAction action) {
1364             if (table.UpdatingCurrent(row, action)) {
1365                 CheckNullable(row);
1366                 CheckMaxLength(row);
1367             }
1368         }
1369
1370         internal bool CheckMaxLength() {
1371             if ((0 <= maxLength) && (null != Table) && (0 < Table.Rows.Count)) {
1372                 Debug.Assert(IsStringType, "not a String or SqlString column");
1373                 foreach (DataRow dr in Table.Rows) {
1374                     if (dr.HasVersion(DataRowVersion.Current)) {
1375                         if (maxLength < GetStringLength(dr.GetCurrentRecordNo())) {
1376                             return false;
1377                         }
1378                     }
1379                 }
1380             }
1381             return true;
1382         }
1383
1384         internal void CheckMaxLength(DataRow dr) {
1385             if (0 <= maxLength) {
1386                 Debug.Assert(IsStringType, "not a String or SqlString column");
1387                 if (maxLength < GetStringLength(dr.GetDefaultRecord())) {
1388                     throw ExceptionBuilder.LongerThanMaxLength(this);
1389                 }
1390             }
1391         }
1392
1393         internal protected void CheckNotAllowNull() {
1394             if (_storage == null)
1395                 return;
1396
1397             if (sortIndex != null) {
1398                 if (sortIndex.IsKeyInIndex(_storage._nullValue)) {// here we do use strong typed NULL for Sql types
1399                     throw ExceptionBuilder.NullKeyValues(ColumnName);
1400                 }
1401             }
1402             else { // since we do not have index, we so sequential search
1403                 foreach (DataRow dr in this.table.Rows) {
1404                     if (dr.RowState == DataRowState.Deleted)
1405                         continue;
1406                     if (!implementsINullable) {
1407                         if (dr[this] == DBNull.Value) {
1408                             throw ExceptionBuilder.NullKeyValues(ColumnName);
1409                         }
1410                     }
1411                     else {
1412                         if (DataStorage.IsObjectNull(dr[this])) {
1413                             throw ExceptionBuilder.NullKeyValues(ColumnName);
1414                         }
1415                     }
1416                 }
1417             }
1418         }
1419
1420         internal void CheckNullable(DataRow row) {
1421             if (!AllowDBNull) {
1422                 Debug.Assert(null != _storage, "no storage");
1423                 if (_storage.IsNull(row.GetDefaultRecord())) {
1424                     throw ExceptionBuilder.NullValues(ColumnName);
1425                 }
1426             }
1427         }
1428
1429         protected void CheckUnique() {
1430             if (!SortIndex.CheckUnique()) {
1431                 // Throws an exception and the name of any column if its Unique property set to
1432                 // True and non-unique values are found in the column.
1433                 throw ExceptionBuilder.NonUniqueValues(ColumnName);
1434             }
1435         }
1436
1437         internal int Compare(int record1, int record2) {
1438             Debug.Assert(null != _storage, "null storage");
1439             return _storage.Compare(record1, record2);
1440         }
1441
1442         internal bool CompareValueTo(int record1, object value, bool checkType) {
1443             // this method is used to make sure value and exact type match.
1444             int valuesMatch = CompareValueTo(record1, value);
1445             // if values match according to storage, do extra checks for exact compare
1446             if (valuesMatch == 0) {
1447                 Type leftType = value.GetType();
1448                 Type rightType = _storage.Get(record1).GetType();
1449                 // if strings, then do exact character by character check
1450                 if (leftType == typeof(System.String) && rightType == typeof(System.String)) {
1451                     return String.CompareOrdinal((string)_storage.Get(record1), (string)value) == 0 ? true : false;
1452                 }
1453                 // make sure same type
1454                 else if (leftType == rightType) {
1455                     return true;
1456                 }
1457             }
1458             return false;
1459         }
1460
1461         internal int CompareValueTo(int record1, object value) {
1462             Debug.Assert(null != _storage, "null storage");
1463             return _storage.CompareValueTo(record1, value);
1464         }
1465
1466         internal object ConvertValue(object value) {
1467             Debug.Assert(null != _storage, "null storage");
1468             return _storage.ConvertValue(value);
1469         }
1470
1471         internal void Copy(int srcRecordNo, int dstRecordNo) {
1472             Debug.Assert(null != _storage, "null storage");
1473             _storage.Copy(srcRecordNo, dstRecordNo);
1474         }
1475
1476         // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
1477         [MethodImpl(MethodImplOptions.NoInlining)] 
1478         internal DataColumn Clone() {
1479             DataColumn clone = (DataColumn)Activator.CreateInstance(this.GetType());
1480             // set All properties
1481             //            clone.columnMapping = columnMapping;
1482
1483             clone.SimpleType = SimpleType;
1484
1485             clone.allowNull = allowNull;
1486             if (null != this.autoInc) {
1487                 clone.autoInc = this.autoInc.Clone();
1488             }
1489             clone.caption = caption;
1490             clone.ColumnName = ColumnName;
1491             clone._columnUri = _columnUri;
1492             clone._columnPrefix = _columnPrefix;
1493             clone.DataType = DataType;
1494             clone.defaultValue = defaultValue;
1495             clone.defaultValueIsNull = ((defaultValue == DBNull.Value) || (clone.ImplementsINullable && DataStorage.IsObjectSqlNull(defaultValue))) ? true : false;
1496             clone.columnMapping = columnMapping;// clone column Mapping since we dont let MaxLength to be set throu API
1497             // 
1498             clone.readOnly = readOnly;
1499             clone.MaxLength = MaxLength;
1500             clone.dttype = dttype;
1501             clone._dateTimeMode = _dateTimeMode;
1502
1503
1504             // so if we have set it, we should continue preserving the information
1505
1506             // ...Extended Properties
1507             if (this.extendedProperties != null) {
1508                 foreach (Object key in this.extendedProperties.Keys) {
1509                     clone.ExtendedProperties[key] = this.extendedProperties[key];
1510                 }
1511             }
1512
1513             return clone;
1514         }
1515
1516         /// <devdoc>
1517         ///    <para>Finds a relation that this column is the sole child of or null.</para>
1518         /// </devdoc>
1519         internal DataRelation FindParentRelation() {
1520             DataRelation[] parentRelations = new DataRelation[Table.ParentRelations.Count];
1521             Table.ParentRelations.CopyTo(parentRelations, 0);
1522
1523             for (int i = 0; i < parentRelations.Length; i++) {
1524                 DataRelation relation = parentRelations[i];
1525                 DataKey key = relation.ChildKey;
1526                 if (key.ColumnsReference.Length == 1 && key.ColumnsReference[0] == this) {
1527                     return relation;
1528                 }
1529             }
1530             // should we throw an exception?
1531             return null;
1532         }
1533
1534
1535         internal object GetAggregateValue(int[] records, AggregateType kind) {
1536             if (_storage == null) {
1537                 if (kind == AggregateType.Count)
1538                     return 0;
1539                 else
1540                     return DBNull.Value;
1541             }
1542             return _storage.Aggregate(records, kind);
1543         }
1544
1545         private int GetStringLength(int record) {
1546             Debug.Assert(null != _storage, "no storage");
1547             return _storage.GetStringLength(record);
1548         }
1549
1550         internal void Init(int record) {
1551             if (AutoIncrement) {
1552                 object value = this.autoInc.Current;
1553                 this.autoInc.MoveAfter();
1554                 Debug.Assert(null != _storage, "no storage");
1555                 _storage.Set(record, value);
1556             }
1557             else
1558                 this[record] = defaultValue;
1559         }
1560
1561         internal static bool IsAutoIncrementType(Type dataType) {
1562             return ((dataType == typeof(Int32)) || (dataType == typeof(Int64)) || (dataType == typeof(Int16)) || (dataType == typeof(Decimal)) || (dataType == typeof(System.Numerics.BigInteger)) ||
1563                    (dataType == typeof(SqlInt32)) || (dataType == typeof(SqlInt64)) || (dataType == typeof(SqlInt16)) || (dataType == typeof(SqlDecimal)));
1564         }
1565
1566         private bool IsColumnMappingValid(StorageType typeCode, MappingType mapping) {
1567             if ((mapping != MappingType.Element) && DataStorage.IsTypeCustomType(typeCode)) {
1568                 return false;
1569             }
1570             return true;
1571         }
1572
1573         internal bool IsCustomType {
1574             get {
1575                 if (null != _storage)
1576                     return _storage._isCustomDefinedType;
1577                 return DataStorage.IsTypeCustomType(DataType);
1578             }
1579         }
1580
1581         internal bool IsValueCustomTypeInstance(object value) {
1582             // if instance is not a storage supported type (built in or SQL types)
1583             return (DataStorage.IsTypeCustomType(value.GetType()) && !(value is Type));
1584         }
1585
1586         internal bool ImplementsIXMLSerializable {
1587             get {
1588                 return implementsIXMLSerializable;
1589             }
1590         }
1591
1592         internal bool IsNull(int record) {
1593             Debug.Assert(null != _storage, "no storage");
1594             return _storage.IsNull(record);
1595         }
1596
1597         /// <devdoc>
1598         ///      Returns true if this column is a part of a Parent or Child key for a relation.
1599         /// </devdoc>
1600         internal bool IsInRelation() {
1601             DataKey key;
1602             DataRelationCollection rels = table.ParentRelations;
1603
1604             Debug.Assert(rels != null, "Invalid ParentRelations");
1605             for (int i = 0; i < rels.Count; i++) {
1606                 key = rels[i].ChildKey;
1607                 Debug.Assert(key.HasValue, "Invalid child key (null)");
1608                 if (key.ContainsColumn(this)) {
1609                     return true;
1610                 }
1611             }
1612             rels = table.ChildRelations;
1613             Debug.Assert(rels != null, "Invalid ChildRelations");
1614             for (int i = 0; i < rels.Count; i++) {
1615                 key = rels[i].ParentKey;
1616                 Debug.Assert(key.HasValue, "Invalid parent key (null)");
1617                 if (key.ContainsColumn(this)) {
1618                     return true;
1619                 }
1620             }
1621             return false;
1622         }
1623
1624         internal bool IsMaxLengthViolated() {
1625             if (MaxLength < 0)
1626                 return true;
1627
1628             bool error = false;
1629             object value;
1630             string errorText = null;
1631
1632             foreach (DataRow dr in Table.Rows) {
1633                 if (dr.HasVersion(DataRowVersion.Current)) {
1634                     value = dr[this];
1635                     if (!this.isSqlType) {
1636                         if (value != null && value != DBNull.Value && ((string)value).Length > MaxLength) {
1637                             if (errorText == null) {
1638                                 errorText = ExceptionBuilder.MaxLengthViolationText(this.ColumnName);
1639                             }
1640                             dr.RowError = errorText;
1641                             dr.SetColumnError(this, errorText);
1642                             error = true;
1643                         }
1644                     }
1645                     else {
1646                         if (!DataStorage.IsObjectNull(value) && ((SqlString)value).Value.Length > MaxLength) {
1647                             if (errorText == null) {
1648                                 errorText = ExceptionBuilder.MaxLengthViolationText(this.ColumnName);
1649                             }
1650                             dr.RowError = errorText;
1651                             dr.SetColumnError(this, errorText);
1652                             error = true;
1653                         }
1654                     }
1655                 }
1656             }
1657             return error;
1658         }
1659
1660         internal bool IsNotAllowDBNullViolated() {//
1661             Index index = this.SortIndex;
1662             DataRow[] rows = index.GetRows(index.FindRecords(DBNull.Value));
1663             for (int i = 0; i < rows.Length; i++) {
1664                 string errorText = ExceptionBuilder.NotAllowDBNullViolationText(this.ColumnName);
1665                 rows[i].RowError = errorText;
1666                 rows[i].SetColumnError(this, errorText);
1667             }
1668             return (rows.Length > 0);
1669         }
1670
1671         internal void FinishInitInProgress() {
1672             if (this.Computed)
1673                 BindExpression();
1674         }
1675
1676         protected virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent) {
1677             if (onPropertyChangingDelegate != null)
1678                 onPropertyChangingDelegate(this, pcevent);
1679         }
1680
1681         protected internal void RaisePropertyChanging(string name) {
1682             OnPropertyChanging(new PropertyChangedEventArgs(name));
1683         }
1684
1685         private void InsureStorage() {
1686             if (_storage == null) {
1687                 _storage = DataStorage.CreateStorage(this, dataType, _storageType);
1688             }
1689         }
1690
1691         internal void SetCapacity(int capacity) {
1692             InsureStorage();
1693             _storage.SetCapacity(capacity);
1694         }
1695
1696         private bool ShouldSerializeDefaultValue() {
1697             return (!DefaultValueIsNull);
1698         }
1699
1700         internal void OnSetDataSet() {
1701         }
1702
1703         // Returns the <see cref='System.Data.DataColumn.Expression'/> of the column, if one exists.
1704         public override string ToString() {
1705             if (this.expression == null)
1706                 return this.ColumnName;
1707             else
1708                 return this.ColumnName + " + " + this.Expression;
1709
1710         }
1711
1712
1713         internal object ConvertXmlToObject(string s) {
1714             Debug.Assert(s != null, "Caller is resposible for missing element/attribure case");
1715             InsureStorage();
1716             return _storage.ConvertXmlToObject(s);
1717         }
1718
1719         internal object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) {
1720             InsureStorage();
1721             return _storage.ConvertXmlToObject(xmlReader, xmlAttrib);
1722         }
1723
1724
1725         internal string ConvertObjectToXml(object value) {
1726             Debug.Assert(value != null && (value != DBNull.Value), "Caller is resposible for checking on DBNull");
1727             InsureStorage();
1728             return _storage.ConvertObjectToXml(value);
1729         }
1730
1731         internal void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) {
1732             Debug.Assert(value != null && (value != DBNull.Value), "Caller is resposible for checking on DBNull");
1733             InsureStorage();
1734             _storage.ConvertObjectToXml(value, xmlWriter, xmlAttrib);
1735         }
1736
1737         internal object GetEmptyColumnStore(int recordCount) {
1738             InsureStorage();
1739             return _storage.GetEmptyStorageInternal(recordCount);
1740         }
1741
1742         internal void CopyValueIntoStore(int record, object store, BitArray nullbits, int storeIndex) {
1743             Debug.Assert(null != _storage, "no storage");
1744             _storage.CopyValueInternal(record, store, nullbits, storeIndex);
1745         }
1746
1747         internal void SetStorage(object store, BitArray nullbits) {
1748             InsureStorage();
1749             _storage.SetStorageInternal(store, nullbits);
1750         }
1751
1752         internal void AddDependentColumn(DataColumn expressionColumn) {
1753             if (dependentColumns == null) {
1754                 dependentColumns = new List<DataColumn>();
1755             }
1756             Debug.Assert(!dependentColumns.Contains(expressionColumn), "duplicate column - expected to be unique");
1757             dependentColumns.Add(expressionColumn);
1758             this.table.AddDependentColumn(expressionColumn);
1759         }
1760
1761         internal void RemoveDependentColumn(DataColumn expressionColumn) {
1762             if (dependentColumns != null && dependentColumns.Contains(expressionColumn)) {
1763                 dependentColumns.Remove(expressionColumn);
1764             }
1765             this.table.RemoveDependentColumn(expressionColumn);
1766         }
1767
1768         internal void HandleDependentColumnList(DataExpression oldExpression, DataExpression newExpression) {
1769             DataColumn[] dependency;
1770             // remove this column from the dependentColumn list of the columns this column depends on.
1771             if (oldExpression != null) {
1772                 dependency = oldExpression.GetDependency();
1773                 foreach (DataColumn col in dependency) {
1774                     Debug.Assert(null != col, "null datacolumn in expression dependencies");
1775                     col.RemoveDependentColumn(this);
1776                     if (col.table != this.table) {
1777                         this.table.RemoveDependentColumn(this);
1778                     }
1779                 }
1780                 this.table.RemoveDependentColumn(this);
1781             }
1782
1783             if (newExpression != null) {
1784                 // get the list of columns that this expression depends on
1785                 dependency = newExpression.GetDependency();
1786                 // add this column to dependent column list of each column this column depends on
1787                 foreach (DataColumn col in dependency) {
1788                     col.AddDependentColumn(this);
1789                     if (col.table != this.table) {
1790                         this.table.AddDependentColumn(this);
1791                     }
1792                 }
1793                 this.table.AddDependentColumn(this);
1794             }
1795         }
1796     }
1797
1798     internal abstract class AutoIncrementValue {
1799         private bool auto;
1800
1801         internal bool Auto {
1802             get { return this.auto; }
1803             set { this.auto = value; }
1804         }
1805         internal abstract object Current { get; set; }
1806         internal abstract long Seed { get; set; }
1807         internal abstract long Step { get; set; }
1808         internal abstract Type DataType { get; }
1809
1810         internal abstract void SetCurrent(object value, IFormatProvider formatProvider);
1811         internal abstract void SetCurrentAndIncrement(object value);
1812         internal abstract void MoveAfter();
1813
1814         internal AutoIncrementValue Clone() {
1815             AutoIncrementValue clone = (this is AutoIncrementInt64) ? (AutoIncrementValue)new AutoIncrementInt64() : (AutoIncrementValue)new AutoIncrementBigInteger();
1816             clone.Auto = this.Auto;
1817             clone.Seed = this.Seed;
1818             clone.Step = this.Step;
1819             clone.Current = this.Current;
1820             return clone;
1821         }
1822     }
1823
1824     /// <summary>the auto stepped value with Int64 representation</summary>
1825     /// <remarks>use unchecked behavior for Dev10 Bug 568510</remarks>
1826     internal sealed class AutoIncrementInt64 : AutoIncrementValue {
1827         /// <summary>the last returned auto incremented value</summary>
1828         private System.Int64 current;
1829
1830         /// <summary>the initial value use to set current</summary>
1831         private System.Int64 seed;
1832
1833         /// <summary>the value by which to offset the next value</summary>
1834         private System.Int64 step = 1;
1835
1836         /// <summary>Gets and sets the current auto incremented value to use</summary>
1837         internal override object Current {
1838             get { return this.current; }
1839             set { this.current = (Int64)value; }
1840         }
1841
1842         internal override Type DataType { get { return typeof(System.Int64); } }
1843
1844         /// <summary>Get and sets the initial seed value.</summary>
1845         internal override long Seed {
1846             get { return this.seed; }
1847             set {
1848                 if ((this.current == this.seed) || this.BoundaryCheck(value)) {
1849                     this.current = value;
1850                 }
1851                 this.seed = value;
1852             }
1853         }
1854
1855         /// <summary>Get and sets the stepping value.</summary>
1856         /// <exception cref="ArugmentException">if value is 0</exception>
1857         internal override long Step {
1858             get { return this.step; }
1859             set {
1860                 if (0 == value) {
1861                     throw ExceptionBuilder.AutoIncrementSeed();
1862                 }
1863                 if (this.step != value) {
1864                     if (this.current != this.Seed) {
1865                         this.current = unchecked(this.current - this.step + value);
1866                     }
1867                     this.step = value;
1868                 }
1869             }
1870         }
1871
1872         internal override void MoveAfter() {
1873             this.current = unchecked(this.current + this.step);
1874         }
1875
1876         internal override void SetCurrent(object value, IFormatProvider formatProvider) {
1877             this.current = Convert.ToInt64(value, formatProvider);
1878         }
1879
1880         internal override void SetCurrentAndIncrement(object value) {
1881             Debug.Assert(null != value && DataColumn.IsAutoIncrementType(value.GetType()) && !(value is System.Numerics.BigInteger), "unexpected value for autoincrement");
1882             System.Int64 v = (Int64)SqlConvert.ChangeType2(value, StorageType.Int64, typeof(Int64), CultureInfo.InvariantCulture);
1883             if (this.BoundaryCheck(v)) {
1884                 this.current = unchecked(v + this.step);
1885             }
1886         }
1887
1888         private bool BoundaryCheck(System.Numerics.BigInteger value) {
1889             return (((this.step < 0) && (value <= this.current)) || ((0 < this.step) && (this.current <= value)));
1890         }
1891     }
1892
1893     /// <summary>the auto stepped value with BigInteger representation</summary>
1894     internal sealed class AutoIncrementBigInteger : AutoIncrementValue {
1895         /// <summary>the current auto incremented value to use</summary>
1896         private System.Numerics.BigInteger current;
1897
1898         /// <summary>the initial value use to set current</summary>
1899         private System.Int64 seed;
1900
1901         /// <summary>the value by which to offset the next value</summary>
1902         private System.Numerics.BigInteger step = 1;
1903
1904         /// <summary>Gets and sets the current auto incremented value to use</summary>
1905         internal override object Current {
1906             get { return this.current; }
1907             set { this.current = (System.Numerics.BigInteger)value; }
1908         }
1909
1910         internal override Type DataType { get { return typeof(System.Numerics.BigInteger); } }
1911
1912         /// <summary>Get and sets the initial seed value.</summary>
1913         internal override long Seed {
1914             get { return this.seed; }
1915             set {
1916                 if ((this.current == this.seed) || this.BoundaryCheck(value)) {
1917                     this.current = value;
1918                 }
1919                 this.seed = value;
1920             }
1921         }
1922
1923         /// <summary>Get and sets the stepping value.</summary>
1924         /// <exception cref="ArugmentException">if value is 0</exception>
1925         internal override long Step {
1926             get { return (long)this.step; }
1927             set {
1928                 if (0 == value) {
1929                     throw ExceptionBuilder.AutoIncrementSeed();
1930                 }
1931                 if (this.step != value) {
1932                     if (this.current != this.Seed) {
1933                         this.current = checked(this.current - this.step + value);
1934                     }
1935                     this.step = value;
1936                 }
1937             }
1938         }
1939
1940         internal override void MoveAfter() {
1941             this.current = checked(this.current + this.step);
1942         }
1943
1944         internal override void SetCurrent(object value, IFormatProvider formatProvider) {
1945             this.current = BigIntegerStorage.ConvertToBigInteger(value, formatProvider);
1946         }
1947
1948         internal override void SetCurrentAndIncrement(object value) {
1949             System.Numerics.BigInteger v = (System.Numerics.BigInteger)value;
1950             if (this.BoundaryCheck(v)) {
1951                 this.current = v + this.step;
1952             }
1953         }
1954
1955         private bool BoundaryCheck(System.Numerics.BigInteger value) {
1956             return (((this.step < 0) && (value <= this.current)) || ((0 < this.step) && (this.current <= value)));
1957         }
1958     }
1959 }