[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System.Data / System / Data / DataTable.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataTable.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.Collections;
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Diagnostics;
15     using System.Globalization;
16     using System.IO;
17     using System.Runtime.Serialization;
18     using System.Text;
19     using System.Threading;
20     using System.Xml;
21     using System.Xml.Schema;
22     using System.Xml.Serialization;
23     using System.Data.Common;
24     using System.Runtime.Versioning;
25     using System.Runtime.CompilerServices;
26
27     /// <devdoc>
28     ///    <para>Represents one table of in-memory data.</para>
29     /// </devdoc>
30     [
31     ToolboxItem(false),
32     DesignTimeVisible(false),
33     DefaultProperty("TableName"),
34     Editor("Microsoft.VSDesigner.Data.Design.DataTableEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
35     DefaultEvent("RowChanging"),
36     XmlSchemaProvider("GetDataTableSchema"),
37     Serializable
38     ]
39     public class DataTable : MarshalByValueComponent, System.ComponentModel.IListSource, ISupportInitializeNotification, ISerializable, IXmlSerializable{
40         private DataSet dataSet;
41         private DataView defaultView = null;
42
43         // rows
44         /// <summary>
45         /// Monotonically increasing number representing the order <see cref="DataRow"/> have been added to <see cref="DataRowCollection"/>.
46         /// </summary>
47         /// <remarks>This limits <see cref="DataRowCollection.Add(DataRow)"/> to <see cref="Int32.MaxValue"/> operations.</remarks>
48         internal long nextRowID;
49         internal readonly DataRowCollection rowCollection;
50
51         // columns
52         internal readonly DataColumnCollection columnCollection;
53
54         // constraints
55         private readonly ConstraintCollection constraintCollection;
56
57         //SimpleContent implementation
58         private int elementColumnCount = 0;
59
60         // relations
61         internal DataRelationCollection parentRelationsCollection;
62         internal DataRelationCollection childRelationsCollection;
63
64         // RecordManager
65         internal readonly RecordManager recordManager;
66
67         // index mgmt
68         internal readonly List<Index> indexes;
69
70         private List<Index> shadowIndexes;
71         private int shadowCount;
72
73         // props
74         internal PropertyCollection extendedProperties = null;
75         private string tableName = "";
76         internal string tableNamespace = null;
77         private string tablePrefix = "";
78         internal DataExpression displayExpression;
79         internal bool fNestedInDataset = true;
80
81         // globalization stuff
82         private CultureInfo _culture;
83         private bool _cultureUserSet;
84         private CompareInfo _compareInfo;
85         private CompareOptions _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
86         private IFormatProvider _formatProvider;
87         private StringComparer _hashCodeProvider;
88         private bool _caseSensitive;
89         private bool _caseSensitiveUserSet;
90
91         // XML properties
92         internal string encodedTableName;           // For XmlDataDocument only
93         internal DataColumn xmlText;            // text values of a complex xml element
94         internal DataColumn _colUnique;
95         internal bool textOnly = false;         // the table has only text value with possible attributes
96         internal decimal minOccurs = 1;    // default = 1
97         internal decimal maxOccurs = 1;    // default = 1
98         internal bool repeatableElement = false;
99         private object typeName = null;
100
101         // primary key info
102         private readonly static Int32[] zeroIntegers = new Int32[0];
103         internal readonly static DataColumn[] zeroColumns = new DataColumn[0];
104         internal readonly static DataRow[] zeroRows = new DataRow[0];
105         internal UniqueConstraint primaryKey;
106         internal readonly static IndexField[] zeroIndexField = new IndexField[0];
107         internal IndexField[] _primaryIndex = zeroIndexField;
108         private DataColumn[] delayedSetPrimaryKey = null;
109
110         // Loading Schema and/or Data related optimization
111         private Index loadIndex;
112         private Index loadIndexwithOriginalAdded = null;
113         private Index loadIndexwithCurrentDeleted = null;
114         private int _suspendIndexEvents;
115
116         private bool savedEnforceConstraints = false;
117         private bool inDataLoad = false;
118         private bool initialLoad;
119         private bool schemaLoading = false;
120         private bool enforceConstraints = true;
121         internal bool _suspendEnforceConstraints = false;
122
123         protected internal bool fInitInProgress = false;
124         private bool inLoad = false;
125         internal bool fInLoadDiffgram = false;
126
127         private byte _isTypedDataTable; // 0 == unknown, 1 = yes, 2 = No
128         private DataRow[] EmptyDataRowArray;
129
130
131         // Property Descriptor Cache for DataBinding
132         private PropertyDescriptorCollection propertyDescriptorCollectionCache = null;
133
134         // Cache for relation that has this table as nested child table.
135         private static readonly DataRelation[] EmptyArrayDataRelation = new DataRelation[0];
136         private DataRelation[] _nestedParentRelations = EmptyArrayDataRelation;
137
138         // Dependent column list for expression evaluation
139         internal List<DataColumn> dependentColumns = null;
140
141         // events
142         private bool mergingData = false;
143         private DataRowChangeEventHandler onRowChangedDelegate;
144         private DataRowChangeEventHandler onRowChangingDelegate;
145         private DataRowChangeEventHandler onRowDeletingDelegate;
146         private DataRowChangeEventHandler onRowDeletedDelegate;
147         private DataColumnChangeEventHandler onColumnChangedDelegate;
148         private DataColumnChangeEventHandler onColumnChangingDelegate;
149
150         private DataTableClearEventHandler onTableClearingDelegate;
151         private DataTableClearEventHandler onTableClearedDelegate;
152         private DataTableNewRowEventHandler onTableNewRowDelegate;
153
154         private PropertyChangedEventHandler onPropertyChangingDelegate;
155
156         private System.EventHandler  onInitialized;
157
158
159         // misc
160         private readonly DataRowBuilder rowBuilder;
161         private const String KEY_XMLSCHEMA = "XmlSchema";
162         private const String KEY_XMLDIFFGRAM = "XmlDiffGram";
163         private const String KEY_NAME = "TableName";
164
165         internal readonly List<DataView> delayedViews = new List<DataView>();
166         private readonly List<DataViewListener> _dataViewListeners = new List<DataViewListener>();
167
168 //        private bool serializeHierarchy = false;
169         internal Hashtable rowDiffId = null;
170         internal readonly ReaderWriterLock indexesLock = new ReaderWriterLock();
171         internal int ukColumnPositionForInference= -1;
172
173         // default remoting format is Xml
174         private SerializationFormat _remotingFormat = SerializationFormat.Xml;
175
176         private static int _objectTypeCount; // Bid counter
177         private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
178
179         /// <devdoc>
180         /// <para>Initializes a new instance of the <see cref='System.Data.DataTable'/> class with no arguments.</para>
181         /// </devdoc>
182         public DataTable() {
183             GC.SuppressFinalize(this);
184             Bid.Trace("<ds.DataTable.DataTable|API> %d#\n", ObjectID);
185             nextRowID = 1;
186             recordManager = new RecordManager(this);
187
188             _culture = CultureInfo.CurrentCulture;
189             this.columnCollection = new DataColumnCollection(this);
190             this.constraintCollection = new ConstraintCollection(this);
191             this.rowCollection = new DataRowCollection(this);
192             this.indexes = new List<Index>();
193
194             rowBuilder = new DataRowBuilder(this, -1);
195         }
196
197         /// <devdoc>
198         /// <para>Intitalizes a new instance of the <see cref='System.Data.DataTable'/> class with the specified table
199         ///    name.</para>
200         /// </devdoc>
201         public DataTable(string tableName) : this() {
202             this.tableName = tableName == null ? "" : tableName;
203         }
204
205         public DataTable(string tableName, string tableNamespace) : this(tableName) {
206             this.Namespace = tableNamespace;
207         }
208
209 //        Deserialize the table from binary/xml stream.
210         protected DataTable(SerializationInfo info, StreamingContext context) : this()
211         {
212             bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
213             SerializationFormat remotingFormat = SerializationFormat.Xml;
214             SerializationInfoEnumerator e = info.GetEnumerator();
215             while (e.MoveNext()) {
216                 switch(e.Name) {
217                     case "DataTable.RemotingFormat" : //DataTable.RemotingFormat does not exist in V1/V1.1 versions
218                     remotingFormat = (SerializationFormat)e.Value;
219                     break;
220                 }
221             }
222
223             DeserializeDataTable(info, context, isSingleTable, remotingFormat);
224         }
225
226         [System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.SerializationFormatter)]
227         public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
228             SerializationFormat remotingFormat = RemotingFormat;
229             bool isSingleTable = context.Context != null ? Convert.ToBoolean(context.Context, CultureInfo.InvariantCulture) : true;
230             SerializeDataTable(info, context, isSingleTable, remotingFormat);
231         }
232
233 //        Serialize the table schema and data.
234         private void SerializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
235             info.AddValue("DataTable.RemotingVersion", new Version(2, 0));
236
237             // SqlHotFix 299, SerializationFormat enumeration types don't exist in V1.1 SP1
238             if (SerializationFormat.Xml != remotingFormat) {
239                 info.AddValue("DataTable.RemotingFormat", remotingFormat);
240             }
241
242             if (remotingFormat != SerializationFormat.Xml) {//Binary
243                 SerializeTableSchema(info, context, isSingleTable);
244                 if (isSingleTable) {
245                     SerializeTableData(info, context, 0);
246                 }
247             } else {//XML/V1.0/V1.1
248                 string tempDSNamespace = "";
249                 Boolean fCreatedDataSet = false;
250
251                 if (dataSet == null) {
252                     DataSet ds = new DataSet("tmpDataSet");
253                     // if user set values on DataTable, it isn't necessary
254                     // to set them on the DataSet because they won't be inherited
255                     // but it is simpler to set them in both places
256
257                     // if user did not set values on DataTable, it is required
258                     // to set them on the DataSet so the table will inherit
259                     // the value already on the Datatable
260                     ds.SetLocaleValue(_culture, _cultureUserSet);
261                     ds.CaseSensitive = this.CaseSensitive;
262                     ds.namespaceURI  = this.Namespace;
263                     Debug.Assert(ds.RemotingFormat == SerializationFormat.Xml, "RemotingFormat must be SerializationFormat.Xml");
264                     ds.Tables.Add(this);
265                     fCreatedDataSet = true;
266                 } else {
267                     tempDSNamespace = this.DataSet.Namespace;
268                     this.DataSet.namespaceURI = this.Namespace; //this.DataSet.Namespace = this.Namespace; ??
269                 }
270
271                 info.AddValue(KEY_XMLSCHEMA, dataSet.GetXmlSchemaForRemoting(this));
272                 info.AddValue(KEY_XMLDIFFGRAM, dataSet.GetRemotingDiffGram(this));
273
274                 if (fCreatedDataSet) {
275                     dataSet.Tables.Remove(this);
276                 }
277                 else{
278                     dataSet.namespaceURI  = tempDSNamespace;
279                 }
280             }
281         }
282
283 //        Deserialize the table schema and data.
284         internal void DeserializeDataTable(SerializationInfo info, StreamingContext context, bool isSingleTable, SerializationFormat remotingFormat) {
285             if (remotingFormat != SerializationFormat.Xml) {//Binary
286                 DeserializeTableSchema(info, context, isSingleTable);
287                 if (isSingleTable) {
288                     DeserializeTableData(info, context, 0);
289                     this.ResetIndexes();
290                 }
291             } else {//XML/V1.0/V1.1
292                 string strSchema = (String)info.GetValue(KEY_XMLSCHEMA, typeof(System.String));
293                 string strData = (String)info.GetValue(KEY_XMLDIFFGRAM, typeof(System.String));
294
295                 if (strSchema != null) {
296                     DataSet ds = new DataSet();
297                     // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
298                     ds.ReadXmlSchema(new XmlTextReader( new StringReader( strSchema ) ) );
299
300                     Debug.Assert(ds.Tables.Count == 1, "There should be exactly 1 table here");
301                     DataTable table = ds.Tables[0];
302                     table.CloneTo(this, null, false);// WebData 111656
303                     //this is to avoid the cascading rules in the namespace
304                     this.Namespace = table.Namespace;
305
306                     if (strData != null) {
307                         ds.Tables.Remove(ds.Tables[0]);
308                         ds.Tables.Add(this);
309                         ds.ReadXml(new XmlTextReader( new StringReader( strData ) ), XmlReadMode.DiffGram);
310                         ds.Tables.Remove(this);
311                     }
312                 }
313             }
314         }
315
316 //        Serialize the columns
317         internal void SerializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
318             //DataTable basic  properties
319             info.AddValue("DataTable.TableName", TableName);
320             info.AddValue("DataTable.Namespace", Namespace);
321             info.AddValue("DataTable.Prefix", Prefix);
322             info.AddValue("DataTable.CaseSensitive", _caseSensitive);
323             info.AddValue("DataTable.caseSensitiveAmbient", !_caseSensitiveUserSet);
324             info.AddValue("DataTable.LocaleLCID", Locale.LCID);
325             info.AddValue("DataTable.MinimumCapacity", recordManager.MinimumCapacity);
326             //info.AddValue("DataTable.DisplayExpression", DisplayExpression);
327
328             //DataTable state internal properties
329             info.AddValue("DataTable.NestedInDataSet", fNestedInDataset);
330             info.AddValue("DataTable.TypeName", TypeName.ToString());
331             info.AddValue("DataTable.RepeatableElement", repeatableElement);
332
333
334             //ExtendedProperties
335             info.AddValue("DataTable.ExtendedProperties", ExtendedProperties);
336
337             //Columns
338             info.AddValue("DataTable.Columns.Count", Columns.Count);
339
340             //Check for closure of expression in case of single table.
341             if (isSingleTable) {
342                 List<DataTable> list = new List<DataTable>();
343                 list.Add(this);
344                 if (!CheckForClosureOnExpressionTables(list))
345                     throw ExceptionBuilder.CanNotRemoteDataTable();
346             }
347
348             IFormatProvider formatProvider = CultureInfo.InvariantCulture;
349             for (int i = 0; i < Columns.Count; i++) {
350                 //DataColumn basic properties
351                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i), Columns[i].ColumnName);
352                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i), Columns[i]._columnUri);
353                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i), Columns[i].Prefix);
354                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), Columns[i].ColumnMapping);
355                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i), Columns[i].AllowDBNull);
356                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i), Columns[i].AutoIncrement);
357                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i), Columns[i].AutoIncrementStep);
358                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i), Columns[i].AutoIncrementSeed);
359                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i), Columns[i].Caption);
360                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), Columns[i].DefaultValue);
361                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i), Columns[i].ReadOnly);
362                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i), Columns[i].MaxLength);
363                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), Columns[i].DataType);
364
365                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), Columns[i].XmlDataType);
366                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), Columns[i].SimpleType);
367
368                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), Columns[i].DateTimeMode);
369
370                 //DataColumn internal state properties
371                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), Columns[i].AutoIncrementCurrent);
372
373                 //Expression
374                 if (isSingleTable) {
375                     info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i), Columns[i].Expression);
376                 }
377
378                 //ExtendedProperties
379                 info.AddValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), Columns[i].extendedProperties);
380             }
381
382             //Constraints
383             if (isSingleTable) {
384                 SerializeConstraints(info, context, 0, false);
385             }
386         }
387
388 //        Deserialize all the Columns
389         internal void DeserializeTableSchema(SerializationInfo info, StreamingContext context, bool isSingleTable) {
390             //DataTable basic properties
391             tableName = info.GetString("DataTable.TableName");
392             tableNamespace = info.GetString("DataTable.Namespace");
393             tablePrefix = info.GetString("DataTable.Prefix");
394
395             bool caseSensitive = info.GetBoolean("DataTable.CaseSensitive");
396             SetCaseSensitiveValue(caseSensitive, true, false);
397             _caseSensitiveUserSet = !info.GetBoolean("DataTable.caseSensitiveAmbient");
398
399             int lcid = (int)info.GetValue("DataTable.LocaleLCID", typeof(int));
400             CultureInfo culture = new CultureInfo(lcid);
401             SetLocaleValue(culture, true, false);
402             _cultureUserSet = true;
403
404
405             MinimumCapacity = info.GetInt32("DataTable.MinimumCapacity");
406             //DisplayExpression = info.GetString("DataTable.DisplayExpression");
407
408             //DataTable state internal properties
409             fNestedInDataset = (bool) info.GetBoolean("DataTable.NestedInDataSet");
410             string tName = info.GetString("DataTable.TypeName");
411             typeName =  new XmlQualifiedName(tName);
412             repeatableElement = info.GetBoolean("DataTable.RepeatableElement");
413
414             //ExtendedProperties
415             extendedProperties = (PropertyCollection) info.GetValue("DataTable.ExtendedProperties", typeof(PropertyCollection));
416
417             //Columns
418             int colCount = info.GetInt32("DataTable.Columns.Count");
419             string [] expressions = new string[colCount];
420             Debug.Assert(Columns.Count == 0, "There is column in Table");
421
422             IFormatProvider formatProvider = CultureInfo.InvariantCulture;
423             for (int i = 0; i < colCount; i++) {
424                 DataColumn dc = new DataColumn();
425
426                 //DataColumn public state properties
427                 dc.ColumnName = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnName", i));
428                 dc._columnUri = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Namespace", i));
429                 dc.Prefix = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Prefix", i));
430
431                 dc.DataType = (Type) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DataType", i), typeof(Type));
432                 dc.XmlDataType = (string) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.XmlDataType", i), typeof(string));
433                 dc.SimpleType = (SimpleType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.SimpleType", i), typeof(SimpleType));
434
435                 dc.ColumnMapping = (MappingType) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ColumnMapping", i), typeof(MappingType));
436                 dc.DateTimeMode = (DataSetDateTime) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DateTimeMode", i), typeof(DataSetDateTime));
437
438                 dc.AllowDBNull = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AllowDBNull", i));
439                 dc.AutoIncrement = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrement", i));
440                 dc.AutoIncrementStep = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementStep", i));
441                 dc.AutoIncrementSeed = info.GetInt64(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementSeed", i));
442                 dc.Caption = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Caption", i));
443                 dc.DefaultValue = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.DefaultValue", i), typeof(object));
444                 dc.ReadOnly = info.GetBoolean(String.Format(formatProvider, "DataTable.DataColumn_{0}.ReadOnly", i));
445                 dc.MaxLength= info.GetInt32(String.Format(formatProvider, "DataTable.DataColumn_{0}.MaxLength", i));
446
447                 //DataColumn internal state properties
448                 dc.AutoIncrementCurrent = info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.AutoIncrementCurrent", i), typeof(object));
449
450                 //Expression
451                 if (isSingleTable) {
452                     expressions[i] = info.GetString(String.Format(formatProvider, "DataTable.DataColumn_{0}.Expression", i));
453                 }
454
455                 //ExtendedProperties
456                 dc.extendedProperties = (PropertyCollection) info.GetValue(String.Format(formatProvider, "DataTable.DataColumn_{0}.ExtendedProperties", i), typeof(PropertyCollection));
457                 Columns.Add(dc);
458             }
459             if (isSingleTable) {
460                 for(int i = 0; i < colCount; i++) {
461                     if (expressions[i] != null) {
462                         Columns[i].Expression = expressions[i];
463                     }
464                 }
465             }
466
467             //Constraints
468             if (isSingleTable) {
469                 DeserializeConstraints(info, context, /*table index */ 0, /* serialize all constraints */false);// since single table, send table index as 0, meanwhile passing
470                 // false for 'allConstraints' means, handle all the constraint related to the table
471             }
472         }
473
474 /*
475         Serialize constraints availabe on the table - note this function is marked internal because it is called by the DataSet deserializer.
476         ***Schema for Serializing ArrayList of Constraints***
477         Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
478         Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
479 */
480         internal void SerializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
481             if (allConstraints) {
482                 Debug.Assert(DataSet != null);
483             }
484
485             ArrayList constraintList = new ArrayList();
486
487             for (int i = 0; i < Constraints.Count; i++) {
488                 Constraint c = Constraints[i];
489
490                 UniqueConstraint uc = c as UniqueConstraint;
491                 if (uc != null) {
492                     int[] colInfo = new int[uc.Columns.Length];
493                     for (int j = 0; j < colInfo.Length; j++) {
494                         colInfo[j] = uc.Columns[j].Ordinal;
495                     }
496
497                     ArrayList list = new ArrayList();
498                     list.Add("U");
499                     list.Add(uc.ConstraintName);
500                     list.Add(colInfo);
501                     list.Add(uc.IsPrimaryKey);
502                     list.Add(uc.ExtendedProperties);
503
504                     constraintList.Add(list);
505                 } else {
506                     ForeignKeyConstraint fk = c as ForeignKeyConstraint;
507                     Debug.Assert(fk != null);
508                     bool shouldSerialize = (allConstraints == true) || (fk.Table == this && fk.RelatedTable == this);
509
510                     if (shouldSerialize) {
511                         int[] parentInfo = new int[fk.RelatedColumns.Length + 1];
512                         parentInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.RelatedTable) : 0;
513                         for (int j = 1; j < parentInfo.Length; j++) {
514                             parentInfo[j] = fk.RelatedColumns[j - 1].Ordinal;
515                         }
516
517                         int[] childInfo = new int[fk.Columns.Length + 1];
518                         childInfo[0] = allConstraints ? this.DataSet.Tables.IndexOf(fk.Table) : 0 ;   //Since the constraint is on the current table, this is the child table.
519                         for (int j = 1; j < childInfo.Length; j++) {
520                             childInfo[j] = fk.Columns[j - 1].Ordinal;
521                         }
522
523                         ArrayList list = new ArrayList();
524                         list.Add("F");
525                         list.Add(fk.ConstraintName);
526                         list.Add(parentInfo);
527                         list.Add(childInfo);
528                         list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
529                         list.Add(fk.ExtendedProperties);
530
531                         constraintList.Add(list);
532                     }
533                 }
534             }
535             info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), constraintList);
536         }
537
538 /*
539         Deserialize the constraints on the table.
540         ***Schema for Serializing ArrayList of Constraints***
541         Unique Constraint - ["U"]->[constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
542         Foriegn Key Constraint - ["F"]->[constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, DeleteRule]->[extendedProperties]
543 */
544         internal void DeserializeConstraints(SerializationInfo info, StreamingContext context, int serIndex, bool allConstraints) {
545             ArrayList constraintList = (ArrayList) info.GetValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.Constraints", serIndex), typeof(ArrayList));
546
547             foreach (ArrayList list in constraintList) {
548                 string con = (string) list[0];
549
550                 if (con.Equals("U")) { //Unique Constraints
551                     string constraintName = (string) list[1];
552
553                     int[] keyColumnIndexes = (int[]) list[2];
554                     bool isPrimaryKey = (bool) list[3];
555                     PropertyCollection extendedProperties = (PropertyCollection) list[4];
556
557                     DataColumn[] keyColumns = new DataColumn[keyColumnIndexes.Length];
558                     for (int i = 0; i < keyColumnIndexes.Length; i++) {
559                         keyColumns[i] = Columns[keyColumnIndexes[i]];
560                     }
561
562                     //Create the constraint.
563                     UniqueConstraint uc = new UniqueConstraint(constraintName, keyColumns, isPrimaryKey);
564                     uc.extendedProperties = extendedProperties;
565
566                     //Add the unique constraint and it will in turn set the primary keys also if needed.
567                     Constraints.Add(uc);
568                 } else { //ForeignKeyConstraints
569                     Debug.Assert(con.Equals("F"));
570
571                     string constraintName = (string) list[1];
572                     int[] parentInfo = (int[]) list[2];
573                     int[] childInfo = (int[]) list[3];
574                     int[] rules = (int[]) list[4];
575                     PropertyCollection extendedProperties = (PropertyCollection) list[5];
576
577                     //ParentKey Columns.
578                     DataTable parentTable = (allConstraints == false) ? this : this.DataSet.Tables[parentInfo[0]];
579                     DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
580                     for (int i = 0; i < parentkeyColumns.Length; i++) {
581                         parentkeyColumns[i] = parentTable.Columns[parentInfo[i + 1]];
582                     }
583
584                     //ChildKey Columns.
585                     DataTable childTable = (allConstraints == false) ? this : this.DataSet.Tables[childInfo[0]];
586                     DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
587                     for (int i = 0; i < childkeyColumns.Length; i++) {
588                         childkeyColumns[i] = childTable.Columns[childInfo[i + 1]];
589                     }
590
591                     //Create the Constraint.
592                     ForeignKeyConstraint fk = new ForeignKeyConstraint(constraintName, parentkeyColumns, childkeyColumns);
593                     fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
594                     fk.UpdateRule = (Rule) rules[1];
595                     fk.DeleteRule = (Rule) rules[2];
596                     fk.extendedProperties = extendedProperties;
597
598                     //Add just the foreign key constraint without creating unique constraint.
599                     Constraints.Add(fk, false);
600                 }
601             }
602         }
603
604 //        Serialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
605         internal void SerializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
606             int colCount = Columns.Count;
607             for (int i = 0; i < colCount; i++) {
608                 info.AddValue(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i), Columns[i].Expression);
609             }
610         }
611
612 //        Deserialize the expressions on the table - Marked internal so that DataSet deserializer can call into this
613         internal void DeserializeExpressionColumns(SerializationInfo info, StreamingContext context, int serIndex) {
614             int colCount = Columns.Count;
615             for (int i = 0; i < colCount; i++) {
616                 string expr = info.GetString(String.Format(CultureInfo.InvariantCulture, "DataTable_{0}.DataColumn_{1}.Expression", serIndex, i));
617                 if (0 != expr.Length) {
618                     Columns[i].Expression = expr;
619                 }
620             }
621         }
622
623 //        Serialize all the Rows.
624         internal void SerializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
625             //Cache all the column count, row count
626             int colCount = Columns.Count;
627             int rowCount = Rows.Count;
628             int modifiedRowCount = 0;
629             int editRowCount = 0;
630
631             //Compute row states and assign the bits accordingly - 00[Unchanged], 01[Added], 10[Modifed], 11[Deleted]
632             BitArray rowStates = new BitArray(rowCount * 3, false); //All bit flags are set to false on initialization of the BitArray.
633             for (int i = 0; i < rowCount; i++) {
634                 int bitIndex = i * 3;
635                 DataRow row = Rows[i];
636                 DataRowState rowState = row.RowState;
637                 switch (rowState) {
638                     case DataRowState.Unchanged:
639                         //rowStates[bitIndex] = false;
640                         //rowStates[bitIndex + 1] = false;
641                         break;
642                     case DataRowState.Added:
643                         //rowStates[bitIndex] = false;
644                         rowStates[bitIndex + 1] = true;
645                         break;
646                     case DataRowState.Modified:
647                         rowStates[bitIndex] = true;
648                         //rowStates[bitIndex + 1] = false;
649                         modifiedRowCount++;
650                         break;
651                     case DataRowState.Deleted:
652                         rowStates[bitIndex] = true;
653                         rowStates[bitIndex + 1] = true;
654                         break;
655                     default:
656                         throw ExceptionBuilder.InvalidRowState(rowState);
657                 }
658                 if (-1 != row.tempRecord) {
659                     rowStates[bitIndex + 2] = true;
660                     editRowCount++;
661                 }
662             }
663
664             //Compute the actual storage records that need to be created.
665             int recordCount = rowCount + modifiedRowCount + editRowCount;
666
667             //Create column storages.
668             ArrayList storeList = new ArrayList();
669             ArrayList nullbitList = new ArrayList();
670             if (recordCount > 0) { //Create the storage only if have records.
671                 for (int i = 0; i < colCount; i++) {
672                     object store = Columns[i].GetEmptyColumnStore(recordCount);
673                     storeList.Add(store);
674                     BitArray nullbits = new BitArray(recordCount);
675                     nullbitList.Add(nullbits);
676                 }
677             }
678
679             //Copy values into column storages
680             int recordsConsumed = 0;
681             Hashtable rowErrors = new Hashtable();
682             Hashtable colErrors = new Hashtable();
683             for (int i = 0; i < rowCount; i++) {
684                 int recordsPerRow = Rows[i].CopyValuesIntoStore(storeList, nullbitList, recordsConsumed);
685                 GetRowAndColumnErrors(i, rowErrors, colErrors);
686                 recordsConsumed += recordsPerRow;
687             }
688
689             IFormatProvider formatProvider = CultureInfo.InvariantCulture;
690             //Serialize all the computed values.
691             info.AddValue(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex), rowCount);
692             info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex), recordCount);
693             info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), rowStates);
694             info.AddValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), storeList);
695             info.AddValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), nullbitList);
696             info.AddValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), rowErrors);
697             info.AddValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), colErrors);
698         }
699
700 //        Deserialize all the Rows.
701         internal void DeserializeTableData(SerializationInfo info, StreamingContext context, int serIndex) {
702             bool enforceConstraintsOrg = enforceConstraints;
703             bool inDataLoadOrg = inDataLoad;
704
705
706             try {
707                 enforceConstraints = false;
708                 inDataLoad = true;
709                 IFormatProvider formatProvider = CultureInfo.InvariantCulture;
710                 int rowCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Rows.Count", serIndex));
711                 int recordCount = info.GetInt32(String.Format(formatProvider, "DataTable_{0}.Records.Count", serIndex));
712                 BitArray rowStates = (BitArray) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowStates", serIndex), typeof(BitArray));
713                 ArrayList storeList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.Records", serIndex), typeof(ArrayList));
714                 ArrayList nullbitList = (ArrayList) info.GetValue(String.Format(formatProvider, "DataTable_{0}.NullBits", serIndex), typeof(ArrayList));
715                 Hashtable rowErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.RowErrors", serIndex), typeof(Hashtable));
716                 rowErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
717                 Hashtable colErrors = (Hashtable) info.GetValue(String.Format(formatProvider, "DataTable_{0}.ColumnErrors", serIndex), typeof(Hashtable));
718                 colErrors.OnDeserialization(this);//OnDeSerialization must be called since the hashtable gets deserialized after the whole graph gets deserialized
719
720
721                 if (recordCount <= 0) { //No need for deserialization of the storage and errors if there are no records.
722                     return;
723                 }
724
725                 //Point the record manager storage to the deserialized values.
726                 for (int i = 0; i < Columns.Count; i++) {
727                     Columns[i].SetStorage(storeList[i], (BitArray) nullbitList[i]);
728                 }
729
730                 //Create rows and set the records appropriately.
731                 int recordIndex = 0;
732                 DataRow[] rowArr = new DataRow[recordCount];
733                 for (int i = 0; i < rowCount; i++) {
734                     //Create a new row which sets old and new records to -1.
735                     DataRow row = NewEmptyRow();
736                     rowArr[recordIndex] = row;
737                     int bitIndex = i * 3;
738                     switch (ConvertToRowState(rowStates, bitIndex)) {
739                         case DataRowState.Unchanged:
740                             row.oldRecord = recordIndex;
741                             row.newRecord = recordIndex;
742                             recordIndex += 1;
743                             break;
744                         case DataRowState.Added:
745                             row.oldRecord = -1;
746                             row.newRecord = recordIndex;
747                             recordIndex += 1;
748                             break;
749                         case DataRowState.Modified:
750                             row.oldRecord = recordIndex;
751                             row.newRecord = recordIndex + 1;
752                             rowArr[recordIndex + 1] = row;
753                             recordIndex += 2;
754                             break;
755                         case DataRowState.Deleted:
756                             row.oldRecord = recordIndex;
757                             row.newRecord = -1;
758                             recordIndex += 1;
759                             break;
760                     }
761                     if (rowStates[bitIndex + 2]) {
762                         row.tempRecord = recordIndex;
763                         rowArr[recordIndex] = row;
764                         recordIndex += 1;
765                     } else {
766                         row.tempRecord = -1;
767                     }
768                     Rows.ArrayAdd(row);
769                     row.rowID = nextRowID;
770                     nextRowID++;
771                     ConvertToRowError(i, rowErrors, colErrors);
772                 }
773                 recordManager.SetRowCache(rowArr);
774                 ResetIndexes();
775             } finally {
776                 enforceConstraints = enforceConstraintsOrg;
777                 inDataLoad = inDataLoadOrg;
778             }
779         }
780
781 //        Constructs the RowState from the two bits in the bitarray.
782         private DataRowState ConvertToRowState(BitArray bitStates, int bitIndex) {
783             Debug.Assert(bitStates != null);
784             Debug.Assert(bitStates.Length > bitIndex);
785
786             bool b1 = bitStates[bitIndex];
787             bool b2 = bitStates[bitIndex + 1];
788
789             if (!b1 && !b2) {
790                 return DataRowState.Unchanged;
791             } else if (!b1 && b2) {
792                 return DataRowState.Added;
793             } else if (b1 && !b2) {
794                 return DataRowState.Modified;
795             } else if (b1 && b2) {
796                 return DataRowState.Deleted;
797             } else {
798                 throw ExceptionBuilder.InvalidRowBitPattern();
799             }
800         }
801
802 //        Get the error on the row and columns - Marked internal so that DataSet deserializer can call into this
803         internal void GetRowAndColumnErrors(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
804             Debug.Assert(Rows.Count > rowIndex);
805             Debug.Assert(rowErrors != null);
806             Debug.Assert(colErrors != null);
807
808             DataRow row = Rows[rowIndex];
809
810             if (row.HasErrors) {
811                 rowErrors.Add(rowIndex, row.RowError);
812                 DataColumn[] dcArr = row.GetColumnsInError();
813                 if (dcArr.Length > 0) {
814                     int[] columnsInError = new int[dcArr.Length];
815                     string[] columnErrors = new string[dcArr.Length];
816                     for (int i = 0; i < dcArr.Length; i++) {
817                         columnsInError[i] = dcArr[i].Ordinal;
818                         columnErrors[i] = row.GetColumnError(dcArr[i]);
819                     }
820                     ArrayList list = new ArrayList();
821                     list.Add(columnsInError);
822                     list.Add(columnErrors);
823                     colErrors.Add(rowIndex, list);
824                 }
825             }
826         }
827
828 //        Set the row and columns in error..
829         private void ConvertToRowError(int rowIndex, Hashtable rowErrors, Hashtable colErrors) {
830             Debug.Assert(Rows.Count > rowIndex);
831             Debug.Assert(rowErrors != null);
832             Debug.Assert(colErrors != null);
833
834             DataRow row = Rows[rowIndex];
835
836             if (rowErrors.ContainsKey(rowIndex)) {
837                 row.RowError = (string) rowErrors[rowIndex];
838             }
839             if (colErrors.ContainsKey(rowIndex)) {
840                 ArrayList list = (ArrayList) colErrors[rowIndex];
841                 int[] columnsInError = (int[]) list[0];
842                 string[] columnErrors = (string[]) list[1];
843                 Debug.Assert(columnsInError.Length == columnErrors.Length);
844                 for (int i = 0; i < columnsInError.Length; i++) {
845                     row.SetColumnError(columnsInError[i], columnErrors[i]);
846                 }
847             }
848         }
849
850         /// <devdoc>
851         ///    <para>Indicates whether string comparisons within the table are case-sensitive.</para>
852         /// </devdoc>
853         [ResDescriptionAttribute(Res.DataTableCaseSensitiveDescr)]
854         public bool CaseSensitive {
855             get {
856                 //The following assert is valid except when calling DataSet.set_CaseSensitive which Validates constraints and failing here
857                 //Debug.Assert(_caseSensitiveUserSet || (null == dataSet) || (dataSet.CaseSensitive == _caseSensitive), "CaseSensitive mismatch");
858                 return _caseSensitive;
859             }
860             set {
861                 if (_caseSensitive != value) {
862                     bool oldValue = _caseSensitive;
863                     bool oldUserSet = _caseSensitiveUserSet;
864                     _caseSensitive = value;
865                     _caseSensitiveUserSet = true;
866
867                     if (DataSet != null && !DataSet.ValidateCaseConstraint()) {
868                         _caseSensitive = oldValue;
869                         _caseSensitiveUserSet = oldUserSet;
870                         throw ExceptionBuilder.CannotChangeCaseLocale();
871                     }
872                     SetCaseSensitiveValue(value, true, true);
873                 }
874                 _caseSensitiveUserSet = true;
875             }
876         }
877
878         internal bool AreIndexEventsSuspended {
879             get { return (0 < _suspendIndexEvents); }
880         }
881
882         internal void RestoreIndexEvents(bool forceReset) {
883             Bid.Trace("<ds.DataTable.RestoreIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
884             if (0 < _suspendIndexEvents) {
885                 _suspendIndexEvents--;
886                 if (0 == _suspendIndexEvents) {
887                     Exception first = null;
888                     SetShadowIndexes();
889                     try{
890                     // the length of shadowIndexes will not change
891                     // but the array instance may change during
892                     // events during Index.Reset
893                         int numIndexes = shadowIndexes.Count;
894                         for (int i = 0; i < numIndexes; i++) {
895                             Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
896                             try {
897                                 if (forceReset || ndx.HasRemoteAggregate) {
898                                     ndx.Reset(); // resets & fires
899                                 }
900                                 else {
901                                     ndx.FireResetEvent(); // fire the Reset event we were firing
902                                 }
903                             }
904                             catch(Exception e) {
905                                 if (!ADP.IsCatchableExceptionType (e)) {
906                                     throw;
907                                 }
908                                 ExceptionBuilder.TraceExceptionWithoutRethrow(e);
909                                 if (null == first) {
910                                     first = e;
911                                 }
912                             }
913                         }
914                         if (null != first) {
915                             throw first;
916                         }
917                     }
918                    finally {
919                        RestoreShadowIndexes();
920                    }
921                 }
922             }
923         }
924
925         internal void SuspendIndexEvents() {
926             Bid.Trace("<ds.DataTable.SuspendIndexEvents|Info> %d#, %d\n", ObjectID, _suspendIndexEvents);
927             _suspendIndexEvents++;
928         }
929
930         [Browsable(false)]
931         public bool IsInitialized {
932             get {
933                 return !fInitInProgress;
934             }
935         }
936
937         private bool IsTypedDataTable {
938             get {
939                 switch (_isTypedDataTable) {
940                 case 0:
941                     _isTypedDataTable = (byte)((this.GetType() != typeof(DataTable))? 1 : 2);
942                     return (1 == _isTypedDataTable);
943                 case 1:
944                     return true;
945                 default:
946                     return false;
947                 }
948             }
949         }
950
951         internal bool SetCaseSensitiveValue(bool isCaseSensitive, bool userSet, bool resetIndexes) {
952             if (userSet || (!_caseSensitiveUserSet && (_caseSensitive != isCaseSensitive))) {
953                 _caseSensitive = isCaseSensitive;
954                 if (isCaseSensitive) {
955                     _compareFlags = CompareOptions.None;
956                 }
957                 else {
958                     _compareFlags = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;
959                 }
960                 if (resetIndexes) {
961                     ResetIndexes();
962                     foreach (Constraint constraint in Constraints) {
963                        constraint.CheckConstraint();
964                     }
965                 }
966                 return true;
967             }
968             return false;
969         }
970
971
972         private void ResetCaseSensitive() {
973             // this method is used design-time scenarios via reflection
974             //   by the property grid context menu to show the Reset option or not
975             SetCaseSensitiveValue((null != dataSet) && dataSet.CaseSensitive, true, true);
976             _caseSensitiveUserSet = false;
977         }
978
979         internal bool ShouldSerializeCaseSensitive() {
980             // this method is used design-time scenarios via reflection
981             //   by the property grid to show the CaseSensitive property in bold or not
982             //   by the code dom for persisting the CaseSensitive property or not
983             return _caseSensitiveUserSet;
984         }
985
986         internal bool SelfNested {
987             get {
988                 // Is this correct? if ((top[i].nestedParentRelation!= null) && (top[i].nestedParentRelation.ParentTable == top[i]))
989                 foreach(DataRelation rel in ParentRelations) {
990                     if (rel.Nested && rel.ParentTable == this) {
991                         return true;
992                     }
993                 }
994                 return false;
995             }
996         }
997 /*        internal bool SelfNestedWithOneRelation {
998             get {
999                 return (this.ParentRelations.Count == 1 && (this.ParentRelations[0].ParentTable == this));
1000             }
1001         }
1002 */
1003
1004         [DebuggerBrowsable(DebuggerBrowsableState.Never)] // don't have debugger view expand this
1005         internal List<Index> LiveIndexes {
1006             get {
1007                 if (!AreIndexEventsSuspended) {
1008                     for (int i = indexes.Count-1; 0 <= i; --i) {
1009                         Index index = indexes[i];
1010                         if (index.RefCount <= 1) {
1011                             index.RemoveRef();
1012                         }
1013                     }
1014                 }
1015                 return indexes;
1016             }
1017         }
1018
1019         [
1020         DefaultValue(SerializationFormat.Xml)
1021         ]
1022         public SerializationFormat RemotingFormat {
1023             get {
1024                 return _remotingFormat;
1025             }
1026             set {
1027                 if (value != SerializationFormat.Binary && value != SerializationFormat.Xml) {
1028                     throw ExceptionBuilder.InvalidRemotingFormat(value);
1029                 }
1030                 // table can not have different format than its dataset, unless it is stand alone datatable
1031                 if (this.DataSet != null && value != this.DataSet.RemotingFormat) {
1032                     throw ExceptionBuilder.CanNotSetRemotingFormat();
1033                 }
1034                 _remotingFormat = value;
1035             }
1036         }
1037
1038 // used to keep temporary state of unique Key posiotion to be added for inference only
1039         internal int UKColumnPositionForInference {
1040             get {
1041                 return ukColumnPositionForInference;
1042             }
1043             set{
1044                 ukColumnPositionForInference= value;
1045             }
1046         }
1047
1048         /// <devdoc>
1049         /// <para>Gets the collection of child relations for this <see cref='System.Data.DataTable'/>.</para>
1050         /// </devdoc>
1051         [
1052         Browsable(false),
1053         ResDescriptionAttribute(Res.DataTableChildRelationsDescr),
1054         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
1055         ]
1056         public DataRelationCollection ChildRelations {
1057             get {
1058                 if (childRelationsCollection == null)
1059                     childRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, false);
1060                 return childRelationsCollection;
1061             }
1062         }
1063
1064         /// <devdoc>
1065         ///    <para>Gets the collection of columns that belong to this table.</para>
1066         /// </devdoc>
1067         [
1068         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1069         ResCategoryAttribute(Res.DataCategory_Data),
1070         ResDescriptionAttribute(Res.DataTableColumnsDescr)
1071         ]
1072         public DataColumnCollection Columns {
1073             get {
1074                 return columnCollection;
1075             }
1076         }
1077
1078         private void ResetColumns() {
1079             // this method is used design-time scenarios via reflection
1080             //   by the property grid context menu to show the Reset option or not
1081             Columns.Clear();
1082         }
1083
1084         private CompareInfo CompareInfo {
1085             get {
1086                 if (null == _compareInfo) {
1087                     _compareInfo = Locale.CompareInfo;
1088                 }
1089                 return _compareInfo;
1090             }
1091         }
1092
1093         /// <devdoc>
1094         ///    <para>Gets the collection of constraints maintained by this table.</para>
1095         /// </devdoc>
1096         [
1097         DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
1098         ResCategoryAttribute(Res.DataCategory_Data),
1099         ResDescriptionAttribute(Res.DataTableConstraintsDescr)
1100         ]
1101         public ConstraintCollection Constraints {
1102             get {
1103                 return constraintCollection;
1104             }
1105         }
1106
1107         /// <devdoc>
1108         ///    <para>
1109         ///       Resets the <see cref='System.Data.DataTable.Constraints'/> property to its default state.
1110         ///    </para>
1111         /// </devdoc>
1112         private void ResetConstraints() {
1113             Constraints.Clear();
1114         }
1115
1116         /// <devdoc>
1117         /// <para>Gets the <see cref='System.Data.DataSet'/> that this table belongs to.</para>
1118         /// </devdoc>
1119         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false), ResDescriptionAttribute(Res.DataTableDataSetDescr)]
1120         public DataSet DataSet {
1121             get {
1122                 return dataSet;
1123             }
1124         }
1125
1126         /// <devdoc>
1127         /// Internal method for setting the DataSet pointer.
1128         /// </devdoc>
1129         internal void SetDataSet(DataSet dataSet) {
1130             if (this.dataSet != dataSet) {
1131                 this.dataSet = dataSet;
1132
1133                 // Inform all the columns of the dataset being set.
1134                 DataColumnCollection   cols = Columns;
1135                 for (int i = 0; i < cols.Count; i++)
1136                     cols[i].OnSetDataSet();
1137
1138                 if (this.DataSet != null) {
1139                     defaultView = null;
1140                 }
1141                 //Set the remoting format variable directly
1142                 if (dataSet != null) {
1143                     _remotingFormat = dataSet.RemotingFormat;
1144                 }
1145             }
1146         }
1147
1148         /// <devdoc>
1149         ///    <para>Gets a customized view of the table which may include a
1150         ///       filtered view, or a cursor position.</para>
1151         /// </devdoc>
1152         [Browsable(false), ResDescriptionAttribute(Res.DataTableDefaultViewDescr)]
1153         public DataView DefaultView {
1154             get {
1155                 DataView view = defaultView;
1156                 if (null == view) {
1157                     if (null != dataSet) {
1158                         view = dataSet.DefaultViewManager.CreateDataView(this);
1159                     }
1160                     else {
1161                         view = new DataView(this, true);
1162                         view.SetIndex2("", DataViewRowState.CurrentRows, null, true);
1163                     }
1164                     // avoid HostProtectionAttribute(Synchronization=true) by not calling virtual methods from inside a lock
1165                     view = Interlocked.CompareExchange<DataView>(ref defaultView, view, null);
1166                     if (null == view) {
1167                         view = defaultView;
1168                     }
1169                 }
1170                 return view;
1171             }
1172         }
1173
1174         /// <devdoc>
1175         ///    <para>Gets or sets the expression that will return a value used to represent
1176         ///       this table in UI.</para>
1177         /// </devdoc>
1178         [
1179         DefaultValue(""),
1180         ResCategoryAttribute(Res.DataCategory_Data),
1181         ResDescriptionAttribute(Res.DataTableDisplayExpressionDescr)
1182         ]
1183         public string DisplayExpression {
1184             get {
1185                 return DisplayExpressionInternal;
1186             }
1187             set {
1188                 if (value != null && value.Length > 0) {
1189                     this.displayExpression = new DataExpression(this, value);
1190                 }
1191                 else {
1192                     this.displayExpression = null;
1193                 }
1194             }
1195         }
1196         internal string DisplayExpressionInternal {
1197             get {
1198                 return(displayExpression != null ? displayExpression.Expression : "");
1199             }
1200         }
1201
1202         internal bool EnforceConstraints {
1203             get {
1204                 if (SuspendEnforceConstraints) {
1205                     return false;
1206                 }
1207                 if (dataSet != null)
1208                     return dataSet.EnforceConstraints;
1209
1210                 return this.enforceConstraints;
1211             }
1212             set {
1213                 if (dataSet == null && this.enforceConstraints != value) {
1214                     if (value)
1215                         EnableConstraints();
1216
1217                     this.enforceConstraints = value;
1218                 }
1219             }
1220         }
1221
1222         internal bool SuspendEnforceConstraints {
1223             get {
1224                 return _suspendEnforceConstraints ;
1225             }
1226             set {
1227                 _suspendEnforceConstraints = value;
1228             }
1229         }
1230
1231         internal void EnableConstraints()
1232         {
1233             bool errors = false;
1234             foreach (Constraint constr in Constraints)
1235             {
1236                 if (constr is UniqueConstraint)
1237                     errors |= constr.IsConstraintViolated();
1238             }
1239
1240             foreach (DataColumn column in Columns) {
1241                 if (!column.AllowDBNull) {
1242                     errors |= column.IsNotAllowDBNullViolated();
1243                 }
1244                 if (column.MaxLength >= 0) {
1245                     errors |= column.IsMaxLengthViolated();
1246                 }
1247             }
1248
1249             if (errors) {
1250                 this.EnforceConstraints = false;
1251                 throw ExceptionBuilder.EnforceConstraint();
1252             }
1253         }
1254
1255         /// <devdoc>
1256         ///    <para>Gets the collection of customized user information.</para>
1257         /// </devdoc>
1258         [
1259         ResCategoryAttribute(Res.DataCategory_Data),
1260         Browsable(false),
1261         ResDescriptionAttribute(Res.ExtendedPropertiesDescr)
1262         ]
1263         public PropertyCollection ExtendedProperties {
1264             get {
1265                 if (extendedProperties == null) {
1266                     extendedProperties = new PropertyCollection();
1267                 }
1268                 return extendedProperties;
1269             }
1270         }
1271
1272         internal IFormatProvider FormatProvider {
1273             get {
1274                 // used for Formating/Parsing
1275                 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemglobalizationcultureinfoclassisneutralculturetopic.asp
1276                 if (null == _formatProvider) {
1277                     CultureInfo culture = Locale;
1278                     if (culture.IsNeutralCulture) {
1279                         culture = CultureInfo.InvariantCulture;
1280                     }
1281                     _formatProvider = (IFormatProvider)culture;
1282                 }
1283                 return _formatProvider;
1284             }
1285         }
1286
1287         /// <devdoc>
1288         ///    <para>Gets a value indicating whether there are errors in any of the rows in any of
1289         ///       the tables of the <see cref='System.Data.DataSet'/> to which the table belongs.</para>
1290         /// </devdoc>
1291         [Browsable(false), ResDescriptionAttribute(Res.DataTableHasErrorsDescr)]
1292         public bool HasErrors {
1293             get {
1294                 for (int i = 0; i < Rows.Count; i++) {
1295                     if (Rows[i].HasErrors) {
1296                         return true;
1297                     }
1298                 }
1299                 return false;
1300             }
1301         }
1302
1303         /// <devdoc>
1304         ///    <para>Gets or sets the locale information used to compare strings within the table.</para>
1305         ///    <para>Also used for locale sensitive, case,kana,width insensitive column name lookups</para>
1306         ///    <para>Also used for converting values to and from string</para>
1307         /// </devdoc>
1308         [ResDescriptionAttribute(Res.DataTableLocaleDescr)]
1309         public CultureInfo Locale {
1310             get {
1311                 // used for Comparing not Formatting/Parsing
1312                 Debug.Assert(null != _culture, "null culture");
1313                 Debug.Assert(_cultureUserSet || (null == dataSet) || _culture.Equals(dataSet.Locale), "Locale mismatch");
1314                 return _culture;
1315             }
1316             set {
1317                 IntPtr hscp;
1318                 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Locale|API> %d#\n", ObjectID);
1319                 try {
1320                     bool userSet = true;
1321                     if (null == value)  {
1322                         // reset Locale to inherit from DataSet
1323                         userSet = false;
1324                         value = (null != dataSet) ? dataSet.Locale : _culture;
1325                     }
1326                     if (_culture != value && !_culture.Equals(value)) {
1327                         bool flag = false;
1328                         bool exceptionThrown = false;
1329                         CultureInfo oldLocale = _culture;
1330                         bool oldUserSet = _cultureUserSet;
1331                         try {
1332                             _cultureUserSet = true;
1333                             SetLocaleValue(value, true, false);
1334                             if ((null == DataSet) || DataSet.ValidateLocaleConstraint()) {
1335                                 flag = false;
1336                                 SetLocaleValue(value, true, true);
1337                                 flag = true;
1338                             }
1339                         }
1340                         catch {
1341                             exceptionThrown = true;
1342                             throw;
1343                         }
1344                         finally {
1345                             if (!flag) { // reset old locale if ValidationFailed or exception thrown
1346                                 try {
1347                                     SetLocaleValue(oldLocale, true, true);
1348                                 }
1349                                 catch(Exception e) { // failed to reset all indexes for all constraints
1350                                     if (!Common.ADP.IsCatchableExceptionType(e)) {
1351                                         throw;
1352                                     }
1353                                     Common.ADP.TraceExceptionWithoutRethrow(e);
1354                                 }
1355                                 _cultureUserSet = oldUserSet;
1356                                 if (!exceptionThrown) {
1357                                     throw ExceptionBuilder.CannotChangeCaseLocale(null);
1358                                 }
1359                             }
1360                         }
1361                         SetLocaleValue(value, true, true);
1362                     }
1363                     _cultureUserSet = userSet;
1364                 }
1365                 finally{
1366                     Bid.ScopeLeave(ref hscp);
1367                 }
1368             }
1369         }
1370
1371         internal bool SetLocaleValue(CultureInfo culture, bool userSet, bool resetIndexes) {
1372             Debug.Assert(null != culture, "SetLocaleValue: no locale");
1373             if (userSet || resetIndexes || (!_cultureUserSet && !_culture.Equals(culture))) {
1374                 _culture = culture;
1375                 _compareInfo = null;
1376                 _formatProvider = null;
1377                 _hashCodeProvider = null;
1378
1379                 foreach(DataColumn column in Columns) {
1380                     column._hashCode = GetSpecialHashCode(column.ColumnName);
1381                 }
1382                 if (resetIndexes) {
1383                     ResetIndexes();
1384                     foreach (Constraint constraint in Constraints) {
1385                         constraint.CheckConstraint();
1386                     }
1387                 }
1388                 return true;
1389             }
1390             return false;
1391         }
1392
1393         internal bool ShouldSerializeLocale() {
1394             // this method is used design-time scenarios via reflection
1395             //   by the property grid to show the Locale property in bold or not
1396             //   by the code dom for persisting the Locale property or not
1397
1398             // we always want the locale persisted if set by user or different the current thread if standalone table
1399             // but that logic should by performed by the serializion code
1400             return _cultureUserSet;
1401         }
1402
1403         /// <devdoc>
1404         ///    <para>Gets or sets the initial starting size for this table.</para>
1405         /// </devdoc>
1406         [
1407         DefaultValue(50),
1408         ResCategoryAttribute(Res.DataCategory_Data),
1409         ResDescriptionAttribute(Res.DataTableMinimumCapacityDescr)
1410         ]
1411         public int MinimumCapacity {
1412             get {
1413                 return recordManager.MinimumCapacity;
1414             }
1415             set {
1416                 if (value != recordManager.MinimumCapacity) {
1417                     recordManager.MinimumCapacity = value;
1418                 }
1419             }
1420         }
1421
1422         internal int RecordCapacity {
1423             get {
1424                 return recordManager.RecordCapacity;
1425             }
1426         }
1427
1428
1429         internal int ElementColumnCount {
1430             get {
1431                 return elementColumnCount;
1432             }
1433             set {
1434                 if ((value > 0) && (xmlText != null))
1435                     throw ExceptionBuilder.TableCannotAddToSimpleContent();
1436                 else elementColumnCount = value;
1437             }
1438         }
1439
1440         /// <devdoc>
1441         /// <para>Gets the collection of parent relations for this <see cref='System.Data.DataTable'/>.</para>
1442         /// </devdoc>
1443         [
1444         Browsable(false),
1445         ResDescriptionAttribute(Res.DataTableParentRelationsDescr),
1446         DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
1447         ]
1448         public DataRelationCollection ParentRelations {
1449             get {
1450                 if (parentRelationsCollection == null)
1451                     parentRelationsCollection = new DataRelationCollection.DataTableRelationCollection(this, true);
1452                 return parentRelationsCollection;
1453             }
1454         }
1455
1456         internal bool MergingData {
1457             get {
1458                 return mergingData;
1459             }
1460             set {
1461                 mergingData = value;
1462             }
1463         }
1464
1465         internal DataRelation[] NestedParentRelations {
1466             get {
1467 #if DEBUG
1468                 DataRelation[] nRel = FindNestedParentRelations();
1469                 Debug.Assert(nRel.Length == _nestedParentRelations.Length, "nestedParent cache is broken");
1470                 for(int i = 0; i < nRel.Length; i++) {
1471                     Debug.Assert(null != nRel[i], "null relation");
1472                     Debug.Assert(null != _nestedParentRelations[i], "null relation");
1473                     Debug.Assert(nRel[i] == _nestedParentRelations[i], "unequal relations");
1474                 }
1475 #endif
1476                 return _nestedParentRelations;
1477             }
1478         }
1479
1480         internal bool SchemaLoading {
1481             get {
1482                 return schemaLoading;
1483             }
1484         }
1485
1486
1487         internal void CacheNestedParent() {
1488             _nestedParentRelations = FindNestedParentRelations();
1489         }
1490
1491         private DataRelation[] FindNestedParentRelations() {
1492             List<DataRelation> nestedParents = null;
1493             foreach(DataRelation relation in this.ParentRelations) {
1494                 if(relation.Nested) {
1495                     if (null == nestedParents) {
1496                         nestedParents = new List<DataRelation>();
1497                     }
1498                     nestedParents.Add(relation);
1499                 }
1500             }
1501             if ((null == nestedParents) || (nestedParents.Count == 0)) {
1502                 return EmptyArrayDataRelation;
1503             }
1504             return nestedParents.ToArray();
1505         }
1506
1507
1508         internal int NestedParentsCount {
1509             get {
1510                 int count = 0;
1511                 foreach(DataRelation relation in this.ParentRelations) {
1512                     if(relation.Nested)
1513                         count++;
1514                 }
1515                 return count;
1516             }
1517         }
1518
1519         /// <devdoc>
1520         ///    <para>Gets or sets an array of columns that function as primary keys for the data
1521         ///       table.</para>
1522         /// </devdoc>
1523         [
1524         TypeConverter(typeof(PrimaryKeyTypeConverter)),
1525         ResDescriptionAttribute(Res.DataTablePrimaryKeyDescr),
1526         ResCategoryAttribute(Res.DataCategory_Data),
1527         Editor("Microsoft.VSDesigner.Data.Design.PrimaryKeyEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing)
1528         ]
1529         public DataColumn[] PrimaryKey {
1530             get {
1531                 UniqueConstraint primayKeyConstraint = primaryKey;
1532                 if (null != primayKeyConstraint) {
1533                     Debug.Assert(2 <= primaryKey.ConstraintIndex.RefCount, "bad primaryKey index RefCount");
1534                     return primayKeyConstraint.Key.ToArray();
1535                 }
1536                 return zeroColumns;
1537             }
1538             set {
1539                 UniqueConstraint key = null;
1540                 UniqueConstraint existingKey = null;
1541
1542                 // Loading with persisted property
1543                 if (fInitInProgress && value != null) {
1544                     delayedSetPrimaryKey = value;
1545                     return;
1546                 }
1547
1548                 if ((value != null) && (value.Length != 0)) {
1549                     int count = 0;
1550                     for (int i = 0; i < value.Length; i++) {
1551                         if (value[i] != null)
1552                             count++;
1553                         else
1554                             break;
1555                     }
1556
1557                     if (count != 0) {
1558                         DataColumn[] newValue = value;
1559                         if (count != value.Length) {
1560                             newValue = new DataColumn[count];
1561                             for (int i = 0; i < count; i++)
1562                                 newValue[i] = value[i];
1563                         }
1564                         key = new UniqueConstraint(newValue);
1565                         if (key.Table != this)
1566                             throw ExceptionBuilder.TableForeignPrimaryKey();
1567                     }
1568                 }
1569
1570                 if (key == primaryKey || (key != null && key.Equals(primaryKey)))
1571                     return;
1572
1573                 // Use an existing UniqueConstraint that matches if one exists
1574                 if ((existingKey = (UniqueConstraint)Constraints.FindConstraint(key)) != null) {
1575                     key.ColumnsReference.CopyTo(existingKey.Key.ColumnsReference, 0);
1576                     key = existingKey;
1577                 }
1578
1579                 UniqueConstraint oldKey = primaryKey;
1580                 primaryKey = null;
1581                 if (oldKey != null) {
1582                     oldKey.ConstraintIndex.RemoveRef();
1583
1584                     // SQLBU 429176: if PrimaryKey is removed, reset LoadDataRow indexes
1585                     if (null != loadIndex) {
1586                         loadIndex.RemoveRef();
1587                         loadIndex = null;
1588                     }
1589                     if (null != loadIndexwithOriginalAdded) {
1590                         loadIndexwithOriginalAdded.RemoveRef();
1591                         loadIndexwithOriginalAdded = null;
1592                     }
1593                     if (null != loadIndexwithCurrentDeleted) {
1594                         loadIndexwithCurrentDeleted.RemoveRef();
1595                         loadIndexwithCurrentDeleted = null;
1596                     }
1597                     Constraints.Remove(oldKey);
1598                 }
1599
1600                 // Add the key if there isnt an existing matching key in collection
1601                 if (key != null && existingKey == null)
1602                     Constraints.Add(key);
1603
1604                 primaryKey = key;
1605
1606                 Debug.Assert(Constraints.FindConstraint(primaryKey) == primaryKey, "PrimaryKey is not in ConstraintCollection");
1607                 _primaryIndex = (key != null) ? key.Key.GetIndexDesc() : zeroIndexField;
1608
1609                 if (primaryKey != null) {
1610                     // must set index for DataView.Sort before setting AllowDBNull which can fail
1611                     key.ConstraintIndex.AddRef();
1612
1613                     for (int i = 0; i < key.ColumnsReference.Length; i++)
1614                         key.ColumnsReference[i].AllowDBNull = false;
1615                 }
1616             }
1617         }
1618
1619         /// <devdoc>
1620         ///    <para>
1621         ///       Indicates whether the <see cref='System.Data.DataTable.PrimaryKey'/> property should be persisted.
1622         ///    </para>
1623         /// </devdoc>
1624         private bool ShouldSerializePrimaryKey() {
1625             return(primaryKey != null);
1626         }
1627
1628         /// <devdoc>
1629         ///    <para>
1630         ///       Resets the <see cref='System.Data.DataTable.PrimaryKey'/> property to its default state.
1631         ///    </para>
1632         /// </devdoc>
1633         private void ResetPrimaryKey() {
1634             PrimaryKey = null;
1635         }
1636
1637         /// <devdoc>
1638         ///    <para>Gets the collection of rows that belong to this table.</para>
1639         /// </devdoc>
1640         [Browsable(false), ResDescriptionAttribute(Res.DataTableRowsDescr)]
1641         public DataRowCollection Rows {
1642             get {
1643                 return rowCollection;
1644             }
1645         }
1646
1647         /// <devdoc>
1648         ///    <para>Gets or sets the name of the table.</para>
1649         /// </devdoc>
1650         [
1651         RefreshProperties(RefreshProperties.All),
1652         DefaultValue(""),
1653         ResCategoryAttribute(Res.DataCategory_Data),
1654         ResDescriptionAttribute(Res.DataTableTableNameDescr)
1655         ]
1656         public string TableName {
1657             get {
1658                 return tableName;
1659             }
1660             set {
1661                 IntPtr hscp;
1662                 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_TableName|API> %d#, value='%ls'\n", ObjectID, value);
1663                 try {
1664                     if (value == null) {
1665                         value = "";
1666                     }
1667                     CultureInfo currentLocale = this.Locale;
1668                     if (String.Compare(tableName, value, true, currentLocale) != 0) {
1669                         if (dataSet != null) {
1670                             if (value.Length == 0)
1671                                 throw ExceptionBuilder.NoTableName();
1672                             if ((0 == String.Compare(value, dataSet.DataSetName, true, dataSet.Locale)) && !fNestedInDataset)
1673                                throw ExceptionBuilder.DatasetConflictingName(dataSet.DataSetName);
1674
1675                             DataRelation[] nestedRelations = NestedParentRelations;
1676                             if (nestedRelations.Length == 0) {
1677                                 dataSet.Tables.RegisterName(value, this.Namespace);
1678                             }
1679                             else {
1680                                 foreach(DataRelation rel in nestedRelations) {
1681                                     if (!rel.ParentTable.Columns.CanRegisterName(value)) {
1682                                         throw ExceptionBuilder.CannotAddDuplicate2(value);
1683                                     }
1684                                 }
1685                                 // if it cannot register the following line will throw exception
1686                                 dataSet.Tables.RegisterName(value, this.Namespace);
1687
1688                                 foreach(DataRelation rel in nestedRelations) {
1689                                     rel.ParentTable.Columns.RegisterColumnName(value, null);
1690                                     rel.ParentTable.Columns.UnregisterName(this.TableName);
1691                                 }
1692                             }
1693
1694                             if (tableName.Length != 0) {
1695                                 dataSet.Tables.UnregisterName(tableName);
1696                             }
1697                         }
1698                         RaisePropertyChanging("TableName");
1699                         tableName = value;
1700                         encodedTableName = null;
1701                     }
1702                     else if (String.Compare(tableName, value, false, currentLocale) != 0) {
1703                         RaisePropertyChanging("TableName");
1704                         tableName = value;
1705                         encodedTableName = null;
1706                     }
1707                 }
1708                 finally {
1709                     Bid.ScopeLeave(ref hscp);
1710                 }
1711             }
1712         }
1713
1714
1715         internal string EncodedTableName {
1716             get {
1717                 string encodedTblName = this.encodedTableName;
1718                 if (null == encodedTblName) {
1719                     encodedTblName = XmlConvert.EncodeLocalName( this.TableName );
1720                     this.encodedTableName = encodedTblName;
1721                 }
1722                 return encodedTblName;
1723             }
1724         }
1725         private string GetInheritedNamespace(List<DataTable> visitedTables){
1726             // if there is nested relation: ie: this table is nested child of a another table and
1727             // if it is not self nested, return parent tables NS: Meanwhile make sure SQLBUDT 240219 is FIXED
1728             DataRelation[] nestedRelations = NestedParentRelations;
1729             if (nestedRelations.Length > 0) {
1730                 for(int i =0; i < nestedRelations.Length; i++) {
1731                     DataRelation rel = nestedRelations[i];
1732                     if (rel.ParentTable.tableNamespace != null) {
1733                         return rel.ParentTable.tableNamespace; // if parent table has a non-null NS, return it
1734                     }
1735                 }
1736                 // Assumption, in hierarchy of multiple nested relation, a child table with no NS, has DataRelation
1737                 // only and only with parent DataTable witin the same namespace
1738                 int j = 0;
1739                 while(j < nestedRelations.Length &&((nestedRelations[j].ParentTable == this)||(visitedTables.Contains(nestedRelations[j].ParentTable)))) {
1740                     j++;
1741                 }
1742                 if (j < nestedRelations.Length) {
1743                     DataTable parentTable = nestedRelations[j].ParentTable;
1744                     if (!visitedTables.Contains(parentTable))
1745                         visitedTables.Add(parentTable);
1746                         return parentTable.GetInheritedNamespace(visitedTables);// this is the same as return parentTable.Namespace
1747                 }
1748             } // dont put else
1749             if (DataSet != null) { // if it cant return from parent tables, return NS from dataset, if exists
1750                 return  DataSet.Namespace;
1751             }
1752             else {
1753                 return string.Empty;
1754             }
1755
1756         }
1757
1758         /// <devdoc>
1759         ///    <para>
1760         ///       Gets or sets the namespace for the <see cref='System.Data.DataTable'/>.
1761         ///    </para>
1762         /// </devdoc>
1763         [
1764         ResCategoryAttribute(Res.DataCategory_Data),
1765         ResDescriptionAttribute(Res.DataTableNamespaceDescr)
1766         ]
1767         public string Namespace {
1768             get {
1769                 if (tableNamespace == null) {
1770                     return GetInheritedNamespace(new List<DataTable>());
1771                 }
1772                 return tableNamespace;
1773             }
1774             set {
1775                 IntPtr hscp;
1776                 Bid.ScopeEnter(out hscp, "<ds.DataTable.set_Namespace|API> %d#, value='%ls'\n", ObjectID, value);
1777                 try {
1778                     if(value != tableNamespace) {
1779                         if (dataSet != null) {
1780                             string realNamespace = (value == null ? GetInheritedNamespace(new List<DataTable>()) : value);
1781                             if (realNamespace != Namespace) {
1782                                 // do this extra check only if the namespace is really going to change
1783                                 // inheritance-wise.
1784                                 if (dataSet.Tables.Contains( this.TableName, realNamespace, true, true))
1785                                     throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
1786
1787                                 CheckCascadingNamespaceConflict(realNamespace);
1788                             }
1789                         }
1790                         CheckNamespaceValidityForNestedRelations(value);
1791                         DoRaiseNamespaceChange();
1792                     }
1793                     tableNamespace = value;
1794                 }
1795                 finally{
1796                     Bid.ScopeLeave(ref hscp);
1797                 }
1798             }
1799         }
1800         internal bool IsNamespaceInherited() {
1801             return (null == tableNamespace);
1802         }
1803
1804         internal void CheckCascadingNamespaceConflict(string realNamespace){
1805             foreach (DataRelation rel in ChildRelations)
1806                 if ((rel.Nested) && (rel.ChildTable != this) && (rel.ChildTable.tableNamespace == null)) {
1807                     DataTable childTable = rel.ChildTable;
1808                     if (dataSet.Tables.Contains( childTable.TableName, realNamespace, false, true))
1809                         throw ExceptionBuilder.DuplicateTableName2(this.TableName, realNamespace);
1810
1811                     childTable.CheckCascadingNamespaceConflict(realNamespace);
1812                 }
1813
1814         }
1815
1816         internal void CheckNamespaceValidityForNestedRelations(string realNamespace){
1817             foreach(DataRelation rel in ChildRelations) {
1818                 if (rel.Nested) {
1819                     if (realNamespace != null) {
1820                         rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(realNamespace, this);
1821                     }
1822                     else{
1823                         rel.ChildTable.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
1824                     }
1825                 }
1826             }
1827             if (realNamespace == null) { // this will affect this table if it has parent relations
1828                 this.CheckNamespaceValidityForNestedParentRelations(GetInheritedNamespace(new List<DataTable>()), this);
1829             }
1830
1831         }
1832         internal void CheckNamespaceValidityForNestedParentRelations(string ns, DataTable parentTable) {
1833             foreach(DataRelation rel in ParentRelations){
1834                 if (rel.Nested) {
1835                     if (rel.ParentTable != parentTable && rel.ParentTable.Namespace != ns) {
1836                         throw ExceptionBuilder.InValidNestedRelation(this.TableName);
1837                     }
1838                 }
1839             }
1840
1841         }
1842
1843         internal void DoRaiseNamespaceChange(){
1844             RaisePropertyChanging("Namespace");
1845             // raise column Namespace change
1846
1847             foreach (DataColumn col in Columns)
1848                 if (col._columnUri == null)
1849                     col.RaisePropertyChanging("Namespace");
1850
1851             foreach (DataRelation rel in ChildRelations)
1852                 if ((rel.Nested) && (rel.ChildTable != this)) {
1853                     DataTable childTable = rel.ChildTable;
1854
1855                     rel.ChildTable.DoRaiseNamespaceChange();
1856                 }
1857         }
1858         /// <devdoc>
1859         ///    <para>
1860         ///       Indicates whether the <see cref='System.Data.DataTable.Namespace'/> property should be persisted.
1861         ///    </para>
1862         /// </devdoc>
1863         private bool ShouldSerializeNamespace() {
1864             return(tableNamespace != null);
1865         }
1866
1867         /// <devdoc>
1868         ///    <para>
1869         ///       Resets the <see cref='System.Data.DataTable.Namespace'/> property to its default state.
1870         ///    </para>
1871         /// </devdoc>
1872         private void ResetNamespace() {
1873             this.Namespace = null;
1874         }
1875
1876         /// <devdoc>
1877         ///    <para>[To be supplied.]</para>
1878         /// </devdoc>
1879         virtual public void BeginInit() {
1880             fInitInProgress = true;
1881         }
1882
1883         /// <devdoc>
1884         ///    <para>[To be supplied.]</para>
1885         /// </devdoc>
1886         virtual public void EndInit() {
1887             if (dataSet == null || !dataSet.fInitInProgress) {
1888                 Columns.FinishInitCollection();
1889                 Constraints.FinishInitConstraints();
1890                 foreach(DataColumn dc in Columns){
1891                     if (dc.Computed) {
1892                         dc.Expression = dc.Expression;
1893                     }
1894                 }
1895             }
1896             fInitInProgress = false; // [....] : 77890. It is must that we set off this flag after calling FinishInitxxx();
1897             if (delayedSetPrimaryKey != null) {
1898                 PrimaryKey = delayedSetPrimaryKey;
1899                 delayedSetPrimaryKey = null;
1900             }
1901             if (delayedViews.Count > 0) {
1902                 foreach(DataView dv in delayedViews) {
1903                     dv.EndInit();
1904                 }
1905                 delayedViews.Clear();
1906             }
1907             OnInitialized();
1908         }
1909
1910         /// <devdoc>
1911         ///    <para>[To be supplied.]</para>
1912         /// </devdoc>
1913         [
1914         DefaultValue(""),
1915         ResCategoryAttribute(Res.DataCategory_Data),
1916         ResDescriptionAttribute(Res.DataTablePrefixDescr)
1917         ]
1918         public string Prefix {
1919             get { return tablePrefix;}
1920             set {
1921                 if (value == null) {
1922                     value = "";
1923                 }
1924                 Bid.Trace("<ds.DataTable.set_Prefix|API> %d#, value='%ls'\n", ObjectID, value);
1925                 if ((XmlConvert.DecodeName(value) == value) &&
1926                     (XmlConvert.EncodeName(value) != value))
1927                     throw ExceptionBuilder.InvalidPrefix(value);
1928
1929
1930                 tablePrefix = value;
1931             }
1932         }
1933
1934         internal DataColumn XmlText {
1935             get {
1936                 return xmlText;
1937             }
1938             set {
1939                 if (xmlText != value) {
1940                     if (xmlText != null) {
1941                         if (value != null) {
1942                             throw ExceptionBuilder.MultipleTextOnlyColumns();
1943                         }
1944                         Columns.Remove(xmlText);
1945                     }
1946                     else {
1947                         Debug.Assert(value != null, "Value shoud not be null ??");
1948                         Debug.Assert(value.ColumnMapping == MappingType.SimpleContent, "should be text node here");
1949                         if (value != Columns[value.ColumnName])
1950                             Columns.Add(value);
1951                     }
1952                     xmlText = value;
1953                 }
1954             }
1955         }
1956
1957         internal decimal MaxOccurs {
1958             get {
1959                 return maxOccurs;
1960             }
1961             set {
1962                 maxOccurs = value;
1963             }
1964         }
1965
1966         internal decimal MinOccurs {
1967             get {
1968                 return minOccurs;
1969             }
1970             set {
1971                 minOccurs = value;
1972             }
1973         }
1974
1975         internal void SetKeyValues(DataKey key, object[] keyValues, int record) {
1976             for (int i = 0; i < keyValues.Length; i++) {
1977                 key.ColumnsReference[i][record] = keyValues[i];
1978             }
1979         }
1980
1981         internal DataRow FindByIndex(Index ndx, object[] key) {
1982             Range range = ndx.FindRecords(key);
1983             if (range.IsNull) {
1984                 return null;
1985             }
1986             return this.recordManager[ndx.GetRecord(range.Min)];
1987         }
1988
1989         internal DataRow FindMergeTarget(DataRow row, DataKey key, Index ndx) {
1990             DataRow targetRow = null;
1991
1992             // Primary key match
1993             if (key.HasValue) {
1994                 Debug.Assert(ndx != null);
1995                 int   findRecord = (row.oldRecord == -1) ? row.newRecord : row.oldRecord;
1996                 object[] values = key.GetKeyValues(findRecord);
1997                 targetRow = FindByIndex(ndx, values);
1998             }
1999             return targetRow;
2000         }
2001
2002         private void SetMergeRecords(DataRow row, int newRecord, int oldRecord, DataRowAction action) {
2003             if (newRecord != -1) {
2004                 SetNewRecord(row, newRecord, action, true, true);
2005                 SetOldRecord(row, oldRecord);
2006             }
2007             else {
2008                 SetOldRecord(row, oldRecord);
2009                 if (row.newRecord != -1) {
2010                     Debug.Assert(action == DataRowAction.Delete, "Unexpected SetNewRecord action in merge function.");
2011                     SetNewRecord(row, newRecord, action, true, true);
2012                 }
2013             }
2014         }
2015
2016         internal DataRow MergeRow(DataRow row, DataRow targetRow, bool preserveChanges, Index idxSearch) {
2017              if (targetRow == null) {
2018                 targetRow = this.NewEmptyRow();
2019                 targetRow.oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
2020                 targetRow.newRecord = targetRow.oldRecord;
2021                 if(row.oldRecord != row.newRecord) {
2022                     targetRow.newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
2023                 }
2024                 InsertRow(targetRow, -1);
2025             }
2026             else {
2027                 // SQLBU 500789: Record Manager corruption during Merge when target row in edit state
2028                 // the newRecord would be freed and overwrite tempRecord (which became the newRecord)
2029                 // this would leave the DataRow referencing a freed record and leaking memory for the now lost record
2030                 int proposedRecord = targetRow.tempRecord; // by saving off the tempRecord, EndEdit won't free newRecord
2031                 targetRow.tempRecord = -1;
2032                 try {
2033                     DataRowState saveRowState = targetRow.RowState;
2034                     int saveIdxRecord = (saveRowState == DataRowState.Added) ? targetRow.newRecord : saveIdxRecord = targetRow.oldRecord;
2035                      int newRecord;
2036                      int oldRecord;
2037                     if (targetRow.RowState == DataRowState.Unchanged && row.RowState == DataRowState.Unchanged) {
2038                         // unchanged row merging with unchanged row
2039                         oldRecord = targetRow.oldRecord;
2040                         newRecord = (preserveChanges) ? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
2041                         oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, targetRow.oldRecord);
2042                         SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
2043                     }
2044                     else if (row.newRecord == -1) {
2045                         // Incoming row is deleted
2046                         oldRecord = targetRow.oldRecord;
2047                         if (preserveChanges) {
2048                           newRecord = (targetRow.RowState == DataRowState.Unchanged)? recordManager.CopyRecord(this, oldRecord, -1) : targetRow.newRecord;
2049                         }
2050                         else
2051                             newRecord = -1;
2052                         oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
2053
2054                         // Change index record, need to update index
2055                         if (saveIdxRecord != ((saveRowState == DataRowState.Added) ? newRecord : oldRecord)) {
2056                             SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
2057                             idxSearch.Reset();
2058                             saveIdxRecord = ((saveRowState == DataRowState.Added) ? newRecord : oldRecord);
2059                         } else {
2060                             SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
2061                         }
2062                     }
2063                     else {
2064                         // incoming row is added, modified or unchanged (targetRow is not unchanged)
2065                         oldRecord = targetRow.oldRecord;
2066                         newRecord = targetRow.newRecord;
2067                         if (targetRow.RowState == DataRowState.Unchanged) {
2068                             newRecord = recordManager.CopyRecord(this, oldRecord, -1);
2069                         }
2070                         oldRecord = recordManager.CopyRecord(row.Table, row.oldRecord, oldRecord);
2071
2072                         if (!preserveChanges) {
2073                             newRecord = recordManager.CopyRecord(row.Table, row.newRecord, newRecord);
2074                         }
2075                         SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
2076                     }
2077
2078                     if (saveRowState == DataRowState.Added && targetRow.oldRecord != -1)
2079                         idxSearch.Reset();
2080                     Debug.Assert(saveIdxRecord == ((saveRowState == DataRowState.Added) ? targetRow.newRecord : targetRow.oldRecord), "oops, you change index record without noticing it");
2081                 }
2082                 finally {
2083                     targetRow.tempRecord = proposedRecord;
2084                 }
2085             }
2086
2087             // Merge all errors
2088             if (row.HasErrors) {
2089                 if (targetRow.RowError.Length == 0) {
2090                     targetRow.RowError = row.RowError;
2091                 } else {
2092                     targetRow.RowError += " ]:[ " + row.RowError;
2093                 }
2094                 DataColumn[] cols = row.GetColumnsInError();
2095
2096                 for (int i = 0; i < cols.Length; i++) {
2097                     DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2098                     targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2099                 }
2100             }else {
2101                 if (!preserveChanges) {
2102                     targetRow.ClearErrors();
2103                 }
2104             }
2105
2106             return targetRow;
2107         }
2108
2109         /// <devdoc>
2110         /// <para>Commits all the changes made to this table since the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
2111         /// </devdoc>
2112         public void AcceptChanges() {
2113             IntPtr hscp;
2114             Bid.ScopeEnter(out hscp, "<ds.DataTable.AcceptChanges|API> %d#\n", ObjectID);
2115             try {
2116                 DataRow[] oldRows = new DataRow[Rows.Count];
2117                 Rows.CopyTo(oldRows, 0);
2118
2119                 // delay updating of indexes until after all
2120                 // AcceptChange calls have been completed
2121                 SuspendIndexEvents();
2122                 try {
2123                     for (int i = 0; i < oldRows.Length; ++i) {
2124                         if (oldRows[i].rowID != -1) {
2125                             oldRows[i].AcceptChanges();
2126                         }
2127                     }
2128                 }
2129                 finally {
2130                     RestoreIndexEvents(false);
2131                 }
2132             }
2133             finally{
2134                 Bid.ScopeLeave(ref hscp);
2135              }
2136         }
2137
2138         // 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.
2139         [MethodImpl(MethodImplOptions.NoInlining)] 
2140         protected virtual DataTable CreateInstance() {
2141             return (DataTable) Activator.CreateInstance(this.GetType(), true);
2142         }
2143
2144         public virtual DataTable Clone() {
2145             return Clone(null);
2146         }
2147
2148         internal DataTable Clone(DataSet cloneDS) {
2149             IntPtr hscp;
2150             Bid.ScopeEnter(out hscp, "<ds.DataTable.Clone|INFO> %d#, cloneDS=%d\n", ObjectID, (cloneDS != null) ? cloneDS.ObjectID : 0);
2151             try {
2152                 DataTable clone = CreateInstance();
2153                 if (clone.Columns.Count > 0) // [....] : To clean up all the schema in strong typed dataset.
2154                     clone.Reset();
2155                 return CloneTo(clone, cloneDS, false);
2156             }
2157             finally {
2158                 Bid.ScopeLeave(ref hscp);
2159             }
2160         }
2161
2162
2163         private DataTable IncrementalCloneTo (DataTable sourceTable, DataTable targetTable) {
2164             foreach(DataColumn dc in sourceTable.Columns) {
2165                 if (targetTable.Columns[dc.ColumnName] == null) {
2166                     targetTable.Columns.Add(dc.Clone());
2167                 }
2168             }
2169
2170             return targetTable;
2171         }
2172
2173         private DataTable CloneHierarchy (DataTable sourceTable, DataSet ds, Hashtable visitedMap) {
2174             if (visitedMap == null)
2175                 visitedMap = new Hashtable();
2176             if (visitedMap.Contains(sourceTable))
2177                 return ((DataTable)visitedMap[sourceTable]);
2178
2179
2180             DataTable destinationTable = ds.Tables[sourceTable.TableName, sourceTable.Namespace];
2181
2182             if ((destinationTable != null && destinationTable.Columns.Count > 0)) {
2183                 destinationTable = IncrementalCloneTo(sourceTable,destinationTable);
2184                    // get extra columns from source into destination , increamental read
2185             }
2186             else {
2187                 if (destinationTable == null) {
2188                     destinationTable = new DataTable();
2189                     // fxcop: new DataTable values for CaseSensitive, Locale, Namespace will come from CloneTo
2190                     ds.Tables.Add(destinationTable);
2191                 }
2192                 destinationTable = sourceTable.CloneTo(destinationTable, ds, true);
2193             }
2194             visitedMap[sourceTable] = destinationTable;
2195
2196
2197             // start cloning relation
2198             foreach( DataRelation r in sourceTable.ChildRelations ) {
2199                 DataTable childTable = CloneHierarchy((DataTable)r.ChildTable, ds, visitedMap);
2200              }
2201
2202             return destinationTable;
2203          }
2204
2205
2206         private DataTable CloneTo(DataTable clone, DataSet cloneDS, bool skipExpressionColumns) {
2207 // we do clone datatables while we do readxmlschema, so we do not want to clone columnexpressions if we call this from ReadXmlSchema
2208 // it will cause exception to be thrown in cae expression refers to a table that is not in hirerachy or not created yet
2209             Debug.Assert(clone != null, "The table passed in has to be newly created empty DataTable.");
2210
2211             // set All properties
2212             clone.tableName = tableName;
2213
2214             clone.tableNamespace = tableNamespace;
2215             clone.tablePrefix = tablePrefix;
2216             clone.fNestedInDataset = fNestedInDataset;
2217
2218             clone._culture = _culture;
2219             clone._cultureUserSet = _cultureUserSet;
2220             clone._compareInfo = _compareInfo;
2221             clone._compareFlags = _compareFlags;
2222             clone._formatProvider = _formatProvider;
2223             clone._hashCodeProvider = _hashCodeProvider;
2224             clone._caseSensitive = _caseSensitive;
2225             clone._caseSensitiveUserSet = _caseSensitiveUserSet;
2226
2227             clone.displayExpression = displayExpression;
2228             clone.typeName = typeName; //[....]
2229             clone.repeatableElement = repeatableElement; //[....]
2230             clone.MinimumCapacity = MinimumCapacity;
2231             clone.RemotingFormat = RemotingFormat;
2232 //            clone.SerializeHierarchy = SerializeHierarchy;
2233
2234             // add all columns
2235             DataColumnCollection clmns = this.Columns;
2236             for (int i = 0; i < clmns.Count; i++) {
2237                 clone.Columns.Add(clmns[i].Clone());
2238             }
2239
2240             // add all expressions if Clone is invoked only on DataTable otherwise DataSet.Clone will assign expressions after creating all relationships.
2241             if (!skipExpressionColumns && cloneDS == null) {
2242                 for (int i = 0; i < clmns.Count; i++) {
2243                     clone.Columns[clmns[i].ColumnName].Expression = clmns[i].Expression;
2244                 }
2245             }
2246
2247             // Create PrimaryKey
2248             DataColumn[] pkey = PrimaryKey;
2249             if (pkey.Length > 0) {
2250                 DataColumn[] key = new DataColumn[pkey.Length];
2251                 for (int i = 0; i < pkey.Length; i++) {
2252                     key[i] = clone.Columns[pkey[i].Ordinal];
2253                 }
2254                 clone.PrimaryKey = key;
2255             }
2256
2257             // now clone all unique constraints
2258             // Rename first
2259             for (int j = 0; j < Constraints.Count; j++)  {
2260                 ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
2261                 UniqueConstraint unique = Constraints[j] as UniqueConstraint;
2262                 if (foreign  != null) {
2263                     if (foreign.Table == foreign.RelatedTable) {
2264                         ForeignKeyConstraint clonedConstraint = foreign.Clone(clone);
2265                         Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
2266                         if (oldConstraint != null) {
2267                             oldConstraint.ConstraintName = Constraints[j].ConstraintName;
2268                         }
2269                     }
2270                 }
2271                 else if (unique != null) {
2272                     UniqueConstraint clonedConstraint = unique.Clone(clone);
2273                     Constraint oldConstraint = clone.Constraints.FindConstraint(clonedConstraint);
2274                     if (oldConstraint != null) {
2275                         oldConstraint.ConstraintName = Constraints[j].ConstraintName;
2276                         foreach (Object key in clonedConstraint.ExtendedProperties.Keys) {
2277                             oldConstraint.ExtendedProperties[key] = clonedConstraint.ExtendedProperties[key];
2278                         }
2279                     }
2280                 }
2281             }
2282
2283             // then add
2284             for (int j = 0; j < Constraints.Count; j++)  {
2285                 if (! clone.Constraints.Contains(Constraints[j].ConstraintName, true)) {
2286                     ForeignKeyConstraint foreign = Constraints[j] as ForeignKeyConstraint;
2287                     UniqueConstraint unique = Constraints[j] as UniqueConstraint;
2288                     if (foreign  != null) {
2289                         if (foreign.Table == foreign.RelatedTable) {
2290                             ForeignKeyConstraint newforeign = foreign.Clone(clone);
2291                             if (newforeign != null) { // we cant make sure that we recieve a cloned FKC,since it depends if table and relatedtable be the same
2292                                 clone.Constraints.Add(newforeign);
2293                             }
2294                         }
2295                     }
2296                     else if (unique != null) {
2297                         clone.Constraints.Add(unique.Clone(clone));
2298                     }
2299                  }
2300             }
2301
2302             // ...Extended Properties...
2303
2304             if (this.extendedProperties != null) {
2305                 foreach(Object key in this.extendedProperties.Keys) {
2306                     clone.ExtendedProperties[key]=this.extendedProperties[key];
2307                 }
2308             }
2309
2310             return clone;
2311         }
2312
2313
2314         public DataTable Copy(){
2315             IntPtr hscp;
2316             Bid.ScopeEnter(out hscp, "<ds.DataTable.Copy|API> %d#\n", ObjectID);
2317             try {
2318                 DataTable destTable = this.Clone();
2319
2320                 foreach (DataRow row in Rows)
2321                     CopyRow(destTable, row);
2322
2323                 return destTable;
2324             }
2325             finally {
2326                 Bid.ScopeLeave(ref hscp);
2327             }
2328         }
2329
2330         /// <devdoc>
2331         ///    <para>Occurs when a value has been submitted for this column.</para>
2332         /// </devdoc>
2333         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangingDescr)]
2334         public event DataColumnChangeEventHandler ColumnChanging {
2335             add {
2336                 Bid.Trace("<ds.DataTable.add_ColumnChanging|API> %d#\n", ObjectID);
2337                 onColumnChangingDelegate += value;
2338             }
2339             remove {
2340                 Bid.Trace("<ds.DataTable.remove_ColumnChanging|API> %d#\n", ObjectID);
2341                 onColumnChangingDelegate -= value;
2342             }
2343         }
2344
2345         /// <devdoc>
2346         ///    <para>[To be supplied.]</para>
2347         /// </devdoc>
2348         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableColumnChangedDescr)]
2349         public event DataColumnChangeEventHandler ColumnChanged {
2350             add  {
2351                 Bid.Trace("<ds.DataTable.add_ColumnChanged|API> %d#\n", ObjectID);
2352                 onColumnChangedDelegate += value;
2353             }
2354             remove {
2355                 Bid.Trace("<ds.DataTable.remove_ColumnChanged|API> %d#\n", ObjectID);
2356                 onColumnChangedDelegate -= value;
2357             }
2358         }
2359
2360         [
2361             ResCategoryAttribute(Res.DataCategory_Action),
2362             ResDescriptionAttribute(Res.DataSetInitializedDescr)
2363         ]
2364         public event System.EventHandler  Initialized {
2365             add {
2366                 onInitialized += value;
2367             }
2368             remove {
2369                 onInitialized -= value;
2370             }
2371         }
2372
2373         internal event PropertyChangedEventHandler PropertyChanging {
2374             add {
2375                 Bid.Trace("<ds.DataTable.add_PropertyChanging|INFO> %d#\n", ObjectID);
2376                 onPropertyChangingDelegate += value;
2377             }
2378             remove {
2379                 Bid.Trace("<ds.DataTable.remove_PropertyChanging|INFO> %d#\n", ObjectID);
2380                 onPropertyChangingDelegate -= value;
2381             }
2382         }
2383
2384         /// <devdoc>
2385         ///    <para>
2386         ///       Occurs after a row in the table has been successfully edited.
2387         ///    </para>
2388         /// </devdoc>
2389         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangedDescr)]
2390         public event DataRowChangeEventHandler RowChanged {
2391             add {
2392                 Bid.Trace("<ds.DataTable.add_RowChanged|API> %d#\n", ObjectID);
2393                 onRowChangedDelegate += value;
2394             }
2395             remove {
2396                 Bid.Trace("<ds.DataTable.remove_RowChanged|API> %d#\n", ObjectID);
2397                 onRowChangedDelegate -= value;
2398             }
2399         }
2400
2401         /// <devdoc>
2402         ///    <para>
2403         ///       Occurs when the <see cref='System.Data.DataRow'/> is changing.
2404         ///    </para>
2405         /// </devdoc>
2406         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowChangingDescr)]
2407         public event DataRowChangeEventHandler RowChanging {
2408             add {
2409                 Bid.Trace("<ds.DataTable.add_RowChanging|API> %d#\n", ObjectID);
2410                 onRowChangingDelegate += value;
2411             }
2412             remove {
2413                 Bid.Trace("<ds.DataTable.remove_RowChanging|API> %d#\n", ObjectID);
2414                 onRowChangingDelegate -= value;
2415             }
2416         }
2417
2418         /// <devdoc>
2419         ///    <para>
2420         ///       Occurs before a row in the table is
2421         ///       about to be deleted.
2422         ///    </para>
2423         /// </devdoc>
2424         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletingDescr)]
2425         public event DataRowChangeEventHandler RowDeleting {
2426             add {
2427                 Bid.Trace("<ds.DataTable.add_RowDeleting|API> %d#\n", ObjectID);
2428                 onRowDeletingDelegate += value;
2429             }
2430             remove {
2431                 Bid.Trace("<ds.DataTable.remove_RowDeleting|API> %d#\n", ObjectID);
2432                 onRowDeletingDelegate -= value;
2433             }
2434         }
2435
2436         /// <devdoc>
2437         ///    <para>
2438         ///       Occurs after a row in the
2439         ///       table has been deleted.
2440         ///    </para>
2441         /// </devdoc>
2442         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowDeletedDescr)]
2443         public event DataRowChangeEventHandler RowDeleted {
2444             add {
2445                 Bid.Trace("<ds.DataTable.add_RowDeleted|API> %d#\n", ObjectID);
2446                 onRowDeletedDelegate += value;
2447             }
2448             remove {
2449                 Bid.Trace("<ds.DataTable.remove_RowDeleted|API> %d#\n", ObjectID);
2450                 onRowDeletedDelegate -= value;
2451             }
2452         }
2453
2454         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearingDescr)]
2455         public event DataTableClearEventHandler TableClearing {
2456             add {
2457                 Bid.Trace("<ds.DataTable.add_TableClearing|API> %d#\n", ObjectID);
2458                 onTableClearingDelegate += value;
2459             }
2460             remove {
2461                 Bid.Trace("<ds.DataTable.remove_TableClearing|API> %d#\n", ObjectID);
2462                 onTableClearingDelegate -= value;
2463             }
2464         }
2465
2466         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsClearedDescr)]
2467         public event DataTableClearEventHandler TableCleared {
2468             add {
2469                 Bid.Trace("<ds.DataTable.add_TableCleared|API> %d#\n", ObjectID);
2470                 onTableClearedDelegate += value;
2471             }
2472             remove {
2473                 Bid.Trace("<ds.DataTable.remove_TableCleared|API> %d#\n", ObjectID);
2474                 onTableClearedDelegate -= value;
2475             }
2476         }
2477
2478         [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataTableRowsNewRowDescr)]
2479         public event DataTableNewRowEventHandler TableNewRow {
2480             add {
2481                 onTableNewRowDelegate += value;
2482             }
2483             remove {
2484                 onTableNewRowDelegate -= value;
2485             }
2486         }
2487
2488         [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2489         public override ISite Site {
2490             get {
2491                 return base.Site;
2492             }
2493             set {
2494                 ISite oldSite = Site;
2495                 if (value == null && oldSite != null) {
2496                     IContainer cont = oldSite.Container;
2497
2498                     if (cont != null) {
2499                         for (int i = 0; i < Columns.Count; i++) {
2500                             if (Columns[i].Site != null) {
2501                                 cont.Remove(Columns[i]);
2502                             }
2503                         }
2504                     }
2505                 }
2506                 base.Site = value;
2507             }
2508         }
2509
2510         internal DataRow AddRecords(int oldRecord, int newRecord) {
2511             DataRow row;
2512             if (oldRecord == -1 && newRecord == -1)
2513             {
2514                 row = NewRow(-1);
2515                 AddRow(row);
2516             }
2517             else
2518             {
2519                 row = NewEmptyRow();
2520                 row.oldRecord = oldRecord;
2521                 row.newRecord = newRecord;
2522                 InsertRow(row, -1);
2523             }
2524             return row;
2525         }
2526
2527         internal void AddRow(DataRow row) {
2528             AddRow(row, -1);
2529         }
2530
2531         internal void AddRow(DataRow row, int proposedID) {
2532             InsertRow(row, proposedID, -1);
2533         }
2534
2535         internal void InsertRow(DataRow row, int proposedID, int pos) {
2536             InsertRow(row, proposedID, pos, /*fireEvent*/true);
2537         }
2538
2539         internal void InsertRow(DataRow row, long proposedID, int pos, bool fireEvent) {
2540             Exception deferredException = null;
2541
2542             if (row == null) {
2543                 throw ExceptionBuilder.ArgumentNull("row");
2544             }
2545             if (row.Table != this) {
2546                 throw ExceptionBuilder.RowAlreadyInOtherCollection();
2547             }
2548             if (row.rowID != -1) {
2549                 throw ExceptionBuilder.RowAlreadyInTheCollection();
2550             }
2551             row.BeginEdit(); // ensure something's there.            
2552
2553             int record = row.tempRecord;
2554             row.tempRecord = -1;
2555
2556             if (proposedID == -1) {
2557                 proposedID = this.nextRowID;
2558             }
2559
2560             bool rollbackOnException;
2561             if (rollbackOnException = (nextRowID <= proposedID)) { // WebData 109005
2562                 nextRowID = checked(proposedID + 1);
2563             }
2564
2565             try {
2566                 try {
2567                     row.rowID = proposedID;
2568                     // this method may cause DataView.OnListChanged in which another row may be added
2569                     SetNewRecordWorker(row, record, DataRowAction.Add, false, false, pos, fireEvent, out deferredException); // now we do add the row to collection before OnRowChanged (RaiseRowChanged)
2570                 }
2571                 catch {
2572                     if (rollbackOnException && (nextRowID == proposedID+1)) {
2573                         nextRowID = proposedID;
2574                     }
2575                     row.rowID = -1;
2576                     row.tempRecord = record;
2577                     throw;
2578                 }
2579
2580                 // since expression evaluation occurred in SetNewRecordWorker, there may have been a problem that
2581                 // was deferred to this point.  If so, throw now since row has already been added.
2582                 if (deferredException != null)
2583                     throw deferredException;
2584
2585                 if (EnforceConstraints && !inLoad ) { // if we are evaluating expression, we need to validate constraints
2586                     int columnCount = columnCollection.Count;
2587                     for (int i = 0; i < columnCount; ++i) {
2588                         DataColumn column = columnCollection[i];
2589                         if (column.Computed) {
2590                             column.CheckColumnConstraint(row, DataRowAction.Add);
2591                         }
2592                     }
2593                 }
2594             }
2595             finally {
2596                 row.ResetLastChangedColumn();// if expression is evaluated while adding, before  return, we want to clear it
2597             }
2598         }
2599
2600         internal void CheckNotModifying(DataRow row) {
2601             if (row.tempRecord != -1) {
2602                 row.EndEdit();
2603                 //throw ExceptionBuilder.ModifyingRow();
2604             }
2605         }
2606
2607         /// <devdoc>
2608         ///    <para>
2609         ///       Clears the table of all data.</para>
2610         /// </devdoc>
2611
2612         public void Clear() {
2613             Clear(true);
2614         }
2615         internal void Clear(bool clearAll) {
2616             IntPtr hscp;
2617             Bid.ScopeEnter(out hscp, "<ds.DataTable.Clear|INFO> %d#, clearAll=%d{bool}\n", ObjectID, clearAll);
2618
2619             try {
2620                 Debug.Assert(null == rowDiffId, "wasn't previously cleared");
2621                 rowDiffId = null;
2622
2623                 if (dataSet != null)
2624                     dataSet.OnClearFunctionCalled(this);
2625                 bool shouldFireClearEvents = (this.Rows.Count != 0); // if Rows is already empty, this is noop
2626
2627                 DataTableClearEventArgs e = null;
2628                 if (shouldFireClearEvents) {
2629                     e = new DataTableClearEventArgs (this);
2630                     OnTableClearing(e);
2631                 }
2632
2633                 if (dataSet != null && dataSet.EnforceConstraints) {
2634
2635                     for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
2636                         ForeignKeyConstraint constraint = constraints.GetForeignKeyConstraint();
2637                         constraint.CheckCanClearParentTable(this);
2638                     }
2639                 }
2640
2641                 recordManager.Clear(clearAll);
2642
2643                 // SQLBU 415729: Serious performance issue when calling Clear()
2644                 // this improves performance by iterating over rows instead of computing by index
2645                 foreach(DataRow row in Rows) {
2646                     row.oldRecord = -1;
2647                     row.newRecord = -1;
2648                     row.tempRecord = -1;
2649                     row.rowID = -1;
2650                     row.RBTreeNodeId = 0;
2651                 }
2652                 Rows.ArrayClear();
2653
2654                 ResetIndexes();
2655
2656                 if (shouldFireClearEvents) {
2657                     OnTableCleared(e);
2658                 }
2659
2660                 // SQLBU 501916 - DataTable internal index is corrupted:'5'
2661                 foreach(DataColumn column in Columns) {
2662                     EvaluateDependentExpressions(column);
2663                 }
2664             }
2665             finally {
2666                 Bid.ScopeLeave(ref hscp);
2667             }
2668         }
2669
2670         internal void CascadeAll(DataRow row, DataRowAction action) {
2671             if (DataSet != null && DataSet.fEnableCascading) {
2672                 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
2673                     constraints.GetForeignKeyConstraint().CheckCascade(row, action);
2674                 }
2675             }
2676         }
2677
2678         internal void CommitRow(DataRow row) {
2679             // Fire Changing event
2680             DataRowChangeEventArgs drcevent = OnRowChanging(null, row, DataRowAction.Commit);
2681
2682             if (!inDataLoad)
2683                 CascadeAll(row, DataRowAction.Commit);
2684
2685             SetOldRecord(row, row.newRecord);
2686
2687             OnRowChanged(drcevent, row, DataRowAction.Commit);
2688         }
2689
2690         internal int Compare(string s1, string s2) {
2691             return Compare(s1, s2, null);
2692         }
2693
2694         internal int Compare(string s1, string s2, CompareInfo comparer) {
2695             object obj1 = s1;
2696             object obj2 = s2;
2697             if (obj1 == obj2)
2698                 return 0;
2699             if (obj1 == null)
2700                 return -1;
2701             if (obj2 == null)
2702                 return 1;
2703
2704             int leng1 = s1.Length;
2705             int leng2 = s2.Length;
2706
2707             for (; leng1 > 0; leng1--) {
2708                 if (s1[leng1-1] != 0x20 && s1[leng1-1] != 0x3000) // 0x3000 is Ideographic Whitespace
2709                     break;
2710             }
2711             for (; leng2 > 0; leng2--) {
2712                 if (s2[leng2-1] != 0x20 && s2[leng2-1] != 0x3000)
2713                     break;
2714             }
2715
2716             return (comparer ?? this.CompareInfo).Compare(s1, 0, leng1, s2, 0, leng2, _compareFlags);
2717         }
2718
2719         internal int IndexOf(string s1, string s2) {
2720             return CompareInfo.IndexOf(s1, s2, _compareFlags);
2721         }
2722
2723         internal bool IsSuffix(string s1, string s2) {
2724             return CompareInfo.IsSuffix(s1, s2, _compareFlags);
2725         }
2726
2727         /// <devdoc>
2728         ///    <para>Computes the given expression on the current rows that pass the filter criteria.</para>
2729         /// </devdoc>
2730         public object Compute(string expression, string filter) {
2731             DataRow[] rows = Select(filter, "", DataViewRowState.CurrentRows);
2732             DataExpression expr = new DataExpression(this, expression);
2733             return expr.Evaluate(rows);
2734         }
2735
2736         bool System.ComponentModel.IListSource.ContainsListCollection {
2737             get {
2738                 return false;
2739             }
2740         }
2741
2742         internal void CopyRow(DataTable table, DataRow row)
2743         {
2744             int oldRecord = -1, newRecord = -1;
2745
2746             if (row == null)
2747                 return;
2748
2749             if (row.oldRecord != -1) {
2750                 oldRecord = table.recordManager.ImportRecord(row.Table, row.oldRecord);
2751             }
2752             if (row.newRecord != -1) {
2753                 if (row.newRecord != row.oldRecord) {
2754                     newRecord = table.recordManager.ImportRecord(row.Table, row.newRecord);
2755                 }
2756                 else
2757                     newRecord = oldRecord;
2758             }
2759
2760             DataRow targetRow = table.AddRecords(oldRecord, newRecord);
2761
2762             if (row.HasErrors) {
2763                 targetRow.RowError = row.RowError;
2764
2765                 DataColumn[] cols = row.GetColumnsInError();
2766
2767                 for (int i = 0; i < cols.Length; i++) {
2768                     DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2769                     targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2770                 }
2771             }
2772
2773        }
2774
2775
2776         internal void DeleteRow(DataRow row) {
2777             if (row.newRecord == -1) {
2778                 throw ExceptionBuilder.RowAlreadyDeleted();
2779             }
2780
2781             // Store.PrepareForDelete(row);
2782             SetNewRecord(row, -1, DataRowAction.Delete, false, true);
2783         }
2784
2785         private void CheckPrimaryKey() {
2786             if (primaryKey == null) throw ExceptionBuilder.TableMissingPrimaryKey();
2787         }
2788
2789         internal DataRow FindByPrimaryKey(object[] values) {
2790             CheckPrimaryKey();
2791             return FindRow(primaryKey.Key, values);
2792         }
2793
2794         internal DataRow FindByPrimaryKey(object value) {
2795             CheckPrimaryKey();
2796             return FindRow(primaryKey.Key, value);
2797         }
2798
2799         private DataRow FindRow(DataKey key, object[] values) {
2800             Index index = GetIndex(NewIndexDesc(key));
2801             Range range = index.FindRecords(values);
2802             if (range.IsNull)
2803                 return null;
2804             return recordManager[index.GetRecord(range.Min)];
2805         }
2806
2807         private DataRow FindRow(DataKey key, object value) {
2808             Index index = GetIndex(NewIndexDesc(key));
2809             Range range = index.FindRecords(value);
2810             if (range.IsNull)
2811                 return null;
2812             return recordManager[index.GetRecord(range.Min)];
2813         }
2814
2815         internal string FormatSortString(IndexField[] indexDesc) {
2816             StringBuilder builder = new StringBuilder();
2817             foreach (IndexField field in indexDesc) {
2818                 if (0 < builder.Length) {
2819                     builder.Append(", ");
2820                 }
2821                 builder.Append(field.Column.ColumnName);
2822                 if (field.IsDescending) {
2823                     builder.Append(" DESC");
2824                 }
2825             }
2826             return builder.ToString();
2827         }
2828
2829         internal void FreeRecord(ref int record) {
2830             recordManager.FreeRecord(ref record);
2831         }
2832
2833         public DataTable GetChanges() {
2834             IntPtr hscp;
2835             Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#\n", ObjectID);
2836             try {
2837                 DataTable dtChanges = this.Clone();
2838                 DataRow row = null;
2839
2840                 for (int i = 0; i < Rows.Count; i++) {
2841                     row = Rows[i];
2842                     if (row.oldRecord != row.newRecord)
2843                         dtChanges.ImportRow(row);
2844                 }
2845
2846                 if (dtChanges.Rows.Count == 0)
2847                     return null;
2848
2849                 return dtChanges;
2850             }
2851             finally {
2852                 Bid.ScopeLeave(ref hscp);
2853             }
2854         }
2855
2856         public DataTable GetChanges(DataRowState rowStates)
2857         {
2858             IntPtr hscp;
2859             Bid.ScopeEnter(out hscp, "<ds.DataTable.GetChanges|API> %d#, rowStates=%d{ds.DataRowState}\n", ObjectID, (int)rowStates);
2860             try {
2861                 DataTable dtChanges = this.Clone();
2862                 DataRow row = null;
2863
2864                 // check that rowStates is valid DataRowState
2865                 Debug.Assert(Enum.GetUnderlyingType(typeof(DataRowState)) == typeof(Int32), "Invalid DataRowState type");
2866
2867                 for (int i = 0; i < Rows.Count; i++) {
2868                     row = Rows[i];
2869                     if ((row.RowState & rowStates) != 0)
2870                         dtChanges.ImportRow(row);
2871                 }
2872
2873                 if (dtChanges.Rows.Count == 0)
2874                     return null;
2875
2876                 return dtChanges;
2877             }
2878             finally {
2879                 Bid.ScopeLeave(ref hscp);
2880             }
2881         }
2882
2883         /// <devdoc>
2884         /// <para>Returns an array of <see cref='System.Data.DataRow'/> objects that contain errors.</para>
2885         /// </devdoc>
2886         public DataRow[] GetErrors() {
2887             List<DataRow> errorList = new List<DataRow>();
2888
2889             for (int i = 0; i < Rows.Count; i++) {
2890                 DataRow row = Rows[i];
2891                 if (row.HasErrors) {
2892                     errorList.Add(row);
2893                 }
2894             }
2895             DataRow[] temp = NewRowArray(errorList.Count);
2896             errorList.CopyTo(temp);
2897             return temp;
2898         }
2899
2900         internal Index GetIndex(IndexField[] indexDesc) {
2901             return GetIndex(indexDesc, DataViewRowState.CurrentRows, (IFilter)null);
2902         }
2903
2904         internal Index GetIndex(string sort, DataViewRowState recordStates, IFilter rowFilter) {
2905             return GetIndex(ParseSortString(sort), recordStates, rowFilter);
2906         }
2907
2908         internal Index GetIndex(IndexField[] indexDesc, DataViewRowState recordStates, IFilter rowFilter) {
2909             indexesLock.AcquireReaderLock(-1);
2910             try {
2911                 for (int i = 0; i < indexes.Count; i++) {
2912                     Index index = indexes[i];
2913                     if (index != null) {
2914                         if (index.Equal(indexDesc, recordStates, rowFilter)) {
2915                             return index;
2916                         }
2917                     }
2918                 }
2919             }
2920             finally {
2921                 indexesLock.ReleaseReaderLock();
2922             }
2923             Index ndx = new Index(this, indexDesc, recordStates, rowFilter);
2924             ndx.AddRef();
2925             return ndx;
2926         }
2927
2928         IList System.ComponentModel.IListSource.GetList() {
2929             return DefaultView;
2930         }
2931
2932
2933         internal List<DataViewListener> GetListeners() {
2934             return _dataViewListeners;
2935         }
2936
2937         // We need a HashCodeProvider for Case, Kana and Width insensitive
2938         internal int GetSpecialHashCode(string name) {
2939             int i;
2940             for (i = 0; (i < name.Length) && (0x3000 > name[i]); ++i);
2941
2942             if (name.Length == i) {
2943                 if (null == _hashCodeProvider) {
2944                     // it should use the CaseSensitive property, but V1 shipped this way
2945                     _hashCodeProvider = StringComparer.Create(Locale, true);
2946                 }
2947                 return _hashCodeProvider.GetHashCode(name);
2948             }
2949             else {
2950                 return 0;
2951             }
2952         }
2953
2954         public void ImportRow(DataRow row)
2955         {
2956             IntPtr hscp;
2957             Bid.ScopeEnter(out hscp, "<ds.DataTable.ImportRow|API> %d#\n", ObjectID);
2958             try {
2959                 int oldRecord = -1, newRecord = -1;
2960
2961                 if (row == null)
2962                     return;
2963
2964                 if (row.oldRecord != -1) {
2965                     oldRecord = recordManager.ImportRecord(row.Table, row.oldRecord);
2966                 }
2967                 if (row.newRecord != -1) {  // row not deleted
2968                     if (row.RowState != DataRowState.Unchanged) { // not unchanged, it means Added or modified
2969                         newRecord = recordManager.ImportRecord(row.Table, row.newRecord);
2970                     }
2971                     else
2972                         newRecord = oldRecord;
2973                 }
2974
2975                 if (oldRecord != -1 || newRecord != -1) {
2976                     DataRow targetRow = AddRecords(oldRecord, newRecord);
2977
2978                     if (row.HasErrors) {
2979                         targetRow.RowError = row.RowError;
2980
2981                         DataColumn[] cols = row.GetColumnsInError();
2982
2983                         for (int i = 0; i < cols.Length; i++) {
2984                             DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
2985                             targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
2986                         }
2987                     }
2988                 }
2989             }
2990             finally {
2991                 Bid.ScopeLeave(ref hscp);
2992             }
2993
2994        }
2995
2996         internal void InsertRow(DataRow row, long proposedID) {
2997             IntPtr hscp;
2998
2999             Bid.ScopeEnter(out hscp, "<ds.DataTable.InsertRow|INFO> %d#, row=%d\n", ObjectID, row.ObjectID);
3000             try {
3001                 if (row.Table != this) {
3002                     throw ExceptionBuilder.RowAlreadyInOtherCollection();
3003                 }
3004                 if (row.rowID != -1) {
3005                     throw ExceptionBuilder.RowAlreadyInTheCollection();
3006                 }
3007                 if (row.oldRecord == -1 && row.newRecord == -1) {
3008                     throw ExceptionBuilder.RowEmpty();
3009                 }
3010
3011                 if (proposedID == -1)
3012                     proposedID = nextRowID;
3013
3014                 row.rowID = proposedID;
3015                 if (nextRowID <= proposedID)
3016                     nextRowID = checked(proposedID + 1);
3017
3018                 DataRowChangeEventArgs drcevent = null;
3019
3020
3021                 if (row.newRecord != -1) {
3022                     row.tempRecord = row.newRecord;
3023                     row.newRecord = -1;
3024
3025                     try {
3026                         drcevent = RaiseRowChanging(null, row, DataRowAction.Add, true);
3027                     }
3028                     catch {
3029                         row.tempRecord = -1;
3030                         throw;
3031                     }
3032
3033                     row.newRecord = row.tempRecord;
3034                     row.tempRecord = -1;
3035                 }
3036
3037                 if (row.oldRecord != -1)
3038                     recordManager[row.oldRecord] = row;
3039
3040                 if (row.newRecord != -1)
3041                     recordManager[row.newRecord] = row;
3042
3043                 Rows.ArrayAdd(row); // SQL BU Defect Tracking 247738, 323482 row should be in the
3044                                     // collection when maintaining the indexes                  
3045
3046                 if (row.RowState == DataRowState.Unchanged){ //  how about row.oldRecord == row.newRecord both == -1
3047                     RecordStateChanged(row.oldRecord, DataViewRowState.None, DataViewRowState.Unchanged);
3048                 }
3049                 else {
3050                     RecordStateChanged(row.oldRecord, DataViewRowState.None, row.GetRecordState(row.oldRecord),
3051                                        row.newRecord, DataViewRowState.None, row.GetRecordState(row.newRecord));
3052                 }
3053
3054                 if (dependentColumns != null && dependentColumns.Count > 0)
3055                     EvaluateExpressions(row, DataRowAction.Add, null);
3056
3057                 RaiseRowChanged(drcevent, row, DataRowAction.Add);
3058             }
3059             finally {
3060                 Bid.ScopeLeave(ref hscp);
3061             }
3062         }
3063
3064         private IndexField [] NewIndexDesc(DataKey key) {
3065             Debug.Assert(key.HasValue);
3066             IndexField[] indexDesc = key.GetIndexDesc();
3067             IndexField[] newIndexDesc = new IndexField[indexDesc.Length];
3068             Array.Copy(indexDesc, 0, newIndexDesc, 0, indexDesc.Length);
3069             return newIndexDesc;
3070         }
3071
3072         internal int NewRecord() {
3073             return NewRecord(-1);
3074         }
3075
3076         internal int NewUninitializedRecord() {
3077             return recordManager.NewRecordBase();
3078         }
3079
3080         internal int NewRecordFromArray(object[] value) {
3081             int colCount = columnCollection.Count; // Perf: use the readonly columnCollection field directly
3082             if (colCount < value.Length) {
3083                 throw ExceptionBuilder.ValueArrayLength();
3084             }
3085             int record = recordManager.NewRecordBase();
3086             try {
3087                 for (int i = 0; i < value.Length; i++) {
3088                     if (null != value[i]) {
3089                         columnCollection[i][record] = value[i];
3090                     }
3091                     else {
3092                         columnCollection[i].Init(record);  // Increase AutoIncrementCurrent
3093                     }
3094                 }
3095                 for (int i = value.Length; i < colCount; i++) {
3096                     columnCollection[i].Init(record);
3097                 }
3098                 return record;
3099             }
3100             catch (Exception e) {
3101                 // 
3102                 if (Common.ADP.IsCatchableOrSecurityExceptionType (e)) {
3103                     FreeRecord(ref record); // WebData 104246
3104                 }
3105                 throw;
3106             }
3107         }
3108
3109         internal int NewRecord(int sourceRecord) {
3110             int record = recordManager.NewRecordBase();
3111
3112             int count = columnCollection.Count;
3113             if (-1 == sourceRecord) {
3114                 for (int i = 0; i < count; ++i) {
3115                     columnCollection[i].Init(record);
3116                 }
3117             }
3118             else {
3119                 for (int i = 0; i < count; ++i) {
3120                     columnCollection[i].Copy(sourceRecord, record);
3121                 }
3122             }
3123             return record;
3124         }
3125
3126         internal DataRow NewEmptyRow() {
3127             rowBuilder._record = -1;
3128             DataRow dr = NewRowFromBuilder( rowBuilder );
3129             if (dataSet != null) {
3130                 DataSet.OnDataRowCreated( dr );
3131             }
3132             return dr;
3133         }
3134
3135         private DataRow NewUninitializedRow() {
3136             DataRow dr = NewRow(NewUninitializedRecord());
3137             return dr;
3138         }
3139
3140         /// <devdoc>
3141         /// <para>Creates a new <see cref='System.Data.DataRow'/>
3142         /// with the same schema as the table.</para>
3143         /// </devdoc>
3144         public DataRow NewRow() {
3145             DataRow dr = NewRow(-1);
3146             NewRowCreated(dr); // this is the only API we want this event to be fired
3147             return dr;
3148         }
3149
3150         // Only initialize DataRelation mapping columns (approximately hidden columns)
3151         internal DataRow CreateEmptyRow() {
3152             DataRow row = this.NewUninitializedRow();
3153
3154             foreach( DataColumn c in this.Columns ) {
3155                 if (!XmlToDatasetMap.IsMappedColumn(c)) {
3156                     if (!c.AutoIncrement) {
3157                         if (c.AllowDBNull) {
3158                             row[c] = DBNull.Value;
3159                         }
3160                         else if(c.DefaultValue!=null){
3161                             row[c] = c.DefaultValue;
3162                         }
3163                     }
3164                     else {
3165                         c.Init(row.tempRecord);
3166                     }
3167                 }
3168             }
3169             return row;
3170         }
3171
3172         private void NewRowCreated(DataRow row) {
3173             if (null != onTableNewRowDelegate) {
3174                 DataTableNewRowEventArgs eventArg =  new DataTableNewRowEventArgs(row);
3175                 OnTableNewRow(eventArg);
3176             }
3177         }
3178
3179         internal DataRow NewRow(int record) {
3180             if (-1 == record) {
3181                 record = NewRecord(-1);
3182             }
3183
3184             rowBuilder._record = record;
3185             DataRow row = NewRowFromBuilder( rowBuilder );
3186             recordManager[record] = row;
3187
3188             if (dataSet != null)
3189                 DataSet.OnDataRowCreated( row );
3190
3191             return row;
3192         }
3193
3194         // This is what a subclassed dataSet overrides to create a new row.
3195         protected virtual DataRow NewRowFromBuilder(DataRowBuilder builder) {
3196             return new DataRow(builder);
3197         }
3198
3199         /// <devdoc>
3200         ///    <para>Gets the row type.</para>
3201         /// </devdoc>
3202         protected virtual Type GetRowType() {
3203             return typeof(DataRow);
3204         }
3205
3206         // 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.
3207         [MethodImpl(MethodImplOptions.NoInlining)] 
3208         protected internal DataRow[] NewRowArray(int size) {
3209             if (IsTypedDataTable) {
3210                 if (0 == size) {
3211                     if (null == EmptyDataRowArray) {
3212                         EmptyDataRowArray = (DataRow[]) Array.CreateInstance(GetRowType(), 0);
3213                     }
3214                     return EmptyDataRowArray;
3215                 }
3216                 return (DataRow[]) Array.CreateInstance(GetRowType(), size);
3217             }
3218             else {
3219                 return ((0 == size) ? DataTable.zeroRows : new DataRow[size]);
3220             }
3221         }
3222
3223         internal bool NeedColumnChangeEvents {
3224             get {
3225                 return (IsTypedDataTable || (null != onColumnChangingDelegate) || (null != onColumnChangedDelegate));
3226             }
3227         }
3228
3229         protected internal virtual void OnColumnChanging(DataColumnChangeEventArgs e) {
3230             // intentionally allow exceptions to bubble up.  We haven't committed anything yet.
3231             Debug.Assert(e != null, "e should not be null");
3232             if (onColumnChangingDelegate != null) {
3233                 Bid.Trace("<ds.DataTable.OnColumnChanging|INFO> %d#\n", ObjectID);
3234                 onColumnChangingDelegate(this, e);
3235             }
3236         }
3237
3238         protected internal virtual void OnColumnChanged(DataColumnChangeEventArgs e) {
3239             Debug.Assert(e != null, "e should not be null");
3240             if (onColumnChangedDelegate != null) {
3241                 Bid.Trace("<ds.DataTable.OnColumnChanged|INFO> %d#\n", ObjectID);
3242                 onColumnChangedDelegate(this, e);
3243             }
3244         }
3245
3246         protected virtual void OnPropertyChanging(PropertyChangedEventArgs pcevent) {
3247             if (onPropertyChangingDelegate != null) {
3248                 Bid.Trace("<ds.DataTable.OnPropertyChanging|INFO> %d#\n", ObjectID);
3249                 onPropertyChangingDelegate(this, pcevent);
3250             }
3251         }
3252
3253         internal void OnRemoveColumnInternal(DataColumn column) {
3254             OnRemoveColumn(column);
3255         }
3256
3257         /// <devdoc>
3258         /// <para>Notifies the <see cref='System.Data.DataTable'/> that a <see cref='System.Data.DataColumn'/> is
3259         ///    being removed.</para>
3260         /// </devdoc>
3261         protected virtual void OnRemoveColumn(DataColumn column) {
3262         }
3263
3264         private DataRowChangeEventArgs OnRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3265             if ((null != onRowChangedDelegate) || IsTypedDataTable) {
3266                 if (null == args) {
3267                     args = new DataRowChangeEventArgs(eRow, eAction);
3268                 }
3269                 OnRowChanged(args);
3270             }
3271             return args;
3272         }
3273
3274         private DataRowChangeEventArgs OnRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3275             if ((null != onRowChangingDelegate) || IsTypedDataTable) {
3276                 if (null == args) {
3277                     args = new DataRowChangeEventArgs(eRow, eAction);
3278                 }
3279                 OnRowChanging(args);
3280             }
3281             return args;
3282         }
3283
3284         /// <devdoc>
3285         ///    <para>
3286         ///       Raises the <see cref='System.Data.DataTable.RowChanged'/> event.
3287         ///    </para>
3288         /// </devdoc>
3289         protected virtual void OnRowChanged(DataRowChangeEventArgs e) {
3290             Debug.Assert((null != e) && ((null != onRowChangedDelegate) || IsTypedDataTable), "OnRowChanged arguments");
3291             if (onRowChangedDelegate != null) {
3292                 Bid.Trace("<ds.DataTable.OnRowChanged|INFO> %d#\n", ObjectID);
3293                 onRowChangedDelegate(this, e);
3294             }
3295         }
3296
3297         /// <devdoc>
3298         ///    <para>
3299         ///       Raises the <see cref='System.Data.DataTable.RowChanging'/> event.
3300         ///    </para>
3301         /// </devdoc>
3302         protected virtual void OnRowChanging(DataRowChangeEventArgs e) {
3303             Debug.Assert((null != e) && ((null != onRowChangingDelegate) || IsTypedDataTable), "OnRowChanging arguments");
3304             if (onRowChangingDelegate != null) {
3305                 Bid.Trace("<ds.DataTable.OnRowChanging|INFO> %d#\n", ObjectID);
3306                 onRowChangingDelegate(this, e);
3307            }
3308         }
3309
3310         /// <devdoc>
3311         ///    <para>
3312         ///       Raises the <see cref='System.Data.DataTable.OnRowDeleting'/> event.
3313         ///    </para>
3314         /// </devdoc>
3315         protected virtual void OnRowDeleting(DataRowChangeEventArgs e) {
3316             Debug.Assert((null != e) && ((null != onRowDeletingDelegate) || IsTypedDataTable), "OnRowDeleting arguments");
3317             if (onRowDeletingDelegate != null) {
3318                 Bid.Trace("<ds.DataTable.OnRowDeleting|INFO> %d#\n", ObjectID);
3319                 onRowDeletingDelegate(this, e);
3320             }
3321         }
3322
3323         /// <devdoc>
3324         ///    <para>
3325         ///       Raises the <see cref='System.Data.DataTable.OnRowDeleted'/> event.
3326         ///    </para>
3327         /// </devdoc>
3328         protected virtual void OnRowDeleted(DataRowChangeEventArgs e) {
3329             Debug.Assert((null != e) && ((null != onRowDeletedDelegate) || IsTypedDataTable), "OnRowDeleted arguments");
3330             if (onRowDeletedDelegate != null) {
3331                 Bid.Trace("<ds.DataTable.OnRowDeleted|INFO> %d#\n", ObjectID);
3332                 onRowDeletedDelegate(this, e);
3333             }
3334         }
3335
3336         protected virtual void OnTableCleared(DataTableClearEventArgs e) {
3337             if (onTableClearedDelegate != null) {
3338                 Bid.Trace("<ds.DataTable.OnTableCleared|INFO> %d#\n", ObjectID);
3339                 onTableClearedDelegate(this, e);
3340             }
3341         }
3342
3343         protected virtual void OnTableClearing(DataTableClearEventArgs e) {
3344             if (onTableClearingDelegate != null) {
3345                 Bid.Trace("<ds.DataTable.OnTableClearing|INFO> %d#\n", ObjectID);
3346                 onTableClearingDelegate(this, e);
3347             }
3348         }
3349
3350         protected virtual void OnTableNewRow(DataTableNewRowEventArgs  e) {
3351             if (onTableNewRowDelegate != null) {
3352                 Bid.Trace("<ds.DataTable.OnTableNewRow|INFO> %d#\n", ObjectID);
3353                 onTableNewRowDelegate(this, e);
3354             }
3355         }
3356
3357         private void OnInitialized() {
3358             if (onInitialized != null) {
3359                 Bid.Trace("<ds.DataTable.OnInitialized|INFO> %d#\n", ObjectID);
3360                 onInitialized(this, EventArgs.Empty);
3361             }
3362         }
3363
3364
3365         internal IndexField[] ParseSortString(string sortString) {
3366             IndexField[] indexDesc = zeroIndexField;
3367             if ((null != sortString) && (0 < sortString.Length)) {
3368                 string[] split = sortString.Split(new char[] { ','});
3369                 indexDesc = new IndexField[split.Length];
3370
3371                 for (int i = 0; i < split.Length; i++) {
3372                     string current = split[i].Trim();
3373
3374                     // handle ASC and DESC.
3375                     int length = current.Length;
3376                     bool descending = false;
3377                     if (length >= 5 && String.Compare(current, length - 4, " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0) {
3378                         current = current.Substring(0, length - 4).Trim();
3379                     }
3380                     else if (length >= 6 && String.Compare(current, length - 5, " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0) {
3381                         descending = true;
3382                         current = current.Substring(0, length - 5).Trim();
3383                     }
3384
3385                     // handle brackets.
3386                     if (current.StartsWith("[", StringComparison.Ordinal)) {
3387                         if (current.EndsWith("]", StringComparison.Ordinal)) {
3388                             current = current.Substring(1, current.Length - 2);
3389                         }
3390                         else {
3391                             throw ExceptionBuilder.InvalidSortString(split[i]);
3392                         }
3393                     }
3394
3395                     // find the column.
3396                     DataColumn column = Columns[current];
3397                     if(column == null) {
3398                         throw ExceptionBuilder.ColumnOutOfRange(current);
3399                     }
3400                     indexDesc[i] = new IndexField(column, descending);
3401                 }
3402             }
3403             return indexDesc;
3404         }
3405
3406         internal void RaisePropertyChanging(string name) {
3407             OnPropertyChanging(new PropertyChangedEventArgs(name));
3408         }
3409
3410         // Notify all indexes that record changed.
3411         // Only called when Error was changed.
3412         internal void RecordChanged(int record) {
3413             Debug.Assert (record != -1, "Record number must be given");
3414             SetShadowIndexes(); // how about new assert?
3415             try {
3416                 int numIndexes = shadowIndexes.Count;
3417                 for (int i = 0; i < numIndexes; i++) {
3418                     Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3419                     if (0 < ndx.RefCount) {
3420                         ndx.RecordChanged(record);
3421                     }
3422                 }
3423             }
3424             finally{
3425                 RestoreShadowIndexes();
3426             }
3427         }
3428
3429 // for each index in liveindexes invok RecordChanged
3430 // oldIndex and newIndex keeps  position of record before delete and after insert in each index in order
3431 // LiveIndexes[n-m] will have its information in oldIndex[n-m] and  newIndex[n-m]
3432         internal void RecordChanged(int[] oldIndex, int[] newIndex) {
3433             SetShadowIndexes();
3434             Debug.Assert (oldIndex.Length == newIndex.Length,  "Size oldIndexes and newIndexes should be the same");
3435             Debug.Assert (oldIndex.Length == shadowIndexes.Count, "Size of OldIndexes should be the same as size of Live indexes");
3436             try{
3437                 int numIndexes = shadowIndexes.Count;
3438                 for (int i = 0; i < numIndexes; i++) {
3439                     Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3440                     if (0 < ndx.RefCount) {
3441                         ndx.RecordChanged(oldIndex[i], newIndex[i]);
3442                     }
3443                 }
3444
3445             }
3446             finally{
3447                 RestoreShadowIndexes();
3448             }
3449         }
3450
3451         internal void RecordStateChanged(int record, DataViewRowState oldState, DataViewRowState newState) {
3452             SetShadowIndexes();
3453             try{
3454                 int numIndexes = shadowIndexes.Count;
3455                 for (int i = 0; i < numIndexes; i++) {
3456                     Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3457                     if (0 < ndx.RefCount) {
3458                         ndx.RecordStateChanged(record, oldState, newState);
3459                     }
3460                 }
3461             }
3462             finally{
3463                 RestoreShadowIndexes();
3464             }
3465             // System.Data.XML.Store.Store.OnROMChanged(record, oldState, newState);
3466         }
3467
3468
3469         internal void RecordStateChanged(int record1, DataViewRowState oldState1, DataViewRowState newState1,
3470                                          int record2, DataViewRowState oldState2, DataViewRowState newState2) {
3471             SetShadowIndexes();
3472             try{
3473                 int numIndexes = shadowIndexes.Count;
3474                 for (int i = 0; i < numIndexes; i++) {
3475                     Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3476                     if (0 < ndx.RefCount) {
3477                         if (record1 != -1 && record2 != -1)
3478                             ndx.RecordStateChanged(record1, oldState1, newState1,
3479                                                    record2, oldState2, newState2);
3480                         else if (record1 != -1)
3481                             ndx.RecordStateChanged(record1, oldState1, newState1);
3482                         else if (record2 != -1)
3483                             ndx.RecordStateChanged(record2, oldState2, newState2);
3484                     }
3485                 }
3486             }
3487             finally {
3488                 RestoreShadowIndexes();
3489             }
3490             // System.Data.XML.Store.Store.OnROMChanged(record1, oldState1, newState1, record2, oldState2, newState2);
3491         }
3492
3493
3494 // RemoveRecordFromIndexes removes the given record (using row and version) from all indexes and it  stores and returns the position of deleted
3495 // record from each index
3496 // IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
3497         internal int[] RemoveRecordFromIndexes(DataRow row, DataRowVersion  version) {
3498             int    indexCount          =  LiveIndexes.Count;
3499             int [] positionIndexes =  new int[indexCount];
3500
3501             int recordNo = row.GetRecordFromVersion(version);
3502             DataViewRowState states = row.GetRecordState(recordNo);
3503
3504             while (--indexCount >= 0) {
3505                 if (row.HasVersion(version) && ((states & indexes[indexCount].RecordStates) != DataViewRowState.None)) {
3506                     int index = indexes[indexCount].GetIndex(recordNo);
3507                     if (index > -1) {
3508                         positionIndexes [indexCount] = index;
3509                         indexes[indexCount].DeleteRecordFromIndex(index); // this will delete the record from index and MUSt not fire event
3510                     }
3511                     else {
3512                         positionIndexes [indexCount] = -1; // this means record was not in index
3513                     }
3514                 }
3515                 else {
3516                     positionIndexes [indexCount] = -1; // this means record was not in index
3517                 }
3518             }
3519             return positionIndexes;
3520         }
3521
3522 // InsertRecordToIndexes inserts the given record (using row and version) to all indexes and it  stores and returns the position of inserted
3523 // record to each index
3524 // IT SHOULD NOT CAUSE ANY EVENT TO BE FIRED
3525         internal int[] InsertRecordToIndexes(DataRow row, DataRowVersion  version) {
3526             int    indexCount          =  LiveIndexes.Count;
3527             int [] positionIndexes =  new int[indexCount];
3528
3529             int recordNo = row.GetRecordFromVersion(version);
3530             DataViewRowState states = row.GetRecordState(recordNo);
3531
3532             while (--indexCount >= 0) {
3533                 if (row.HasVersion(version)) {
3534                     if ((states & indexes[indexCount].RecordStates) != DataViewRowState.None) {
3535                         positionIndexes [indexCount] = indexes[indexCount].InsertRecordToIndex(recordNo);
3536                     }
3537                     else {
3538                         positionIndexes [indexCount] = -1;
3539                     }
3540                 }
3541             }
3542             return positionIndexes;
3543         }
3544
3545         internal void SilentlySetValue(DataRow dr, DataColumn dc, DataRowVersion version, object newValue) {
3546             // get record for version
3547             int record = dr.GetRecordFromVersion(version);
3548
3549             bool equalValues = false;
3550             if (DataStorage.IsTypeCustomType(dc.DataType) && newValue != dc[record]) {
3551                 // if UDT storage, need to check if reference changed. See bug 385182
3552                 equalValues = false;
3553             }
3554             else {
3555                 equalValues = dc.CompareValueTo(record, newValue, true);
3556             }
3557
3558             // if expression has changed
3559             if (!equalValues) {
3560                 int[] oldIndex = dr.Table.RemoveRecordFromIndexes(dr, version);// conditional, if it exists it will try to remove with no event fired
3561                 dc.SetValue(record, newValue);
3562                 int[] newIndex = dr.Table.InsertRecordToIndexes(dr, version);// conditional, it will insert if it qualifies, no event will be fired
3563                 if (dr.HasVersion(version)) {
3564                     if (version != DataRowVersion.Original) {
3565                         dr.Table.RecordChanged(oldIndex, newIndex);
3566                     }
3567                     if (dc.dependentColumns != null) {
3568                         //BugBug - passing in null for cachedRows.  This means expression columns as keys does not work when key changes.
3569                         dc.Table.EvaluateDependentExpressions(dc.dependentColumns, dr, version, null);
3570                     }
3571                 }
3572              }
3573              dr.ResetLastChangedColumn();
3574         }
3575
3576         /// <devdoc>
3577         ///    <para>Rolls back all changes that have been made to the table
3578         ///       since it was loaded, or the last time <see cref='System.Data.DataTable.AcceptChanges'/> was called.</para>
3579         /// </devdoc>
3580         public void RejectChanges() {
3581             IntPtr hscp;
3582             Bid.ScopeEnter(out hscp, "<ds.DataTable.RejectChanges|API> %d#\n", ObjectID);
3583             try{
3584                 DataRow[] oldRows = new DataRow[Rows.Count];
3585                 Rows.CopyTo(oldRows, 0);
3586
3587                 for (int i = 0; i < oldRows.Length; i++) {
3588                     RollbackRow(oldRows[i]);
3589                 }
3590             }
3591             finally{
3592                 Bid.ScopeLeave(ref hscp);
3593             }
3594
3595         }
3596
3597         internal void RemoveRow(DataRow row, bool check) {
3598             if (row.rowID == -1) {
3599                 throw ExceptionBuilder.RowAlreadyRemoved();
3600             }
3601
3602             if (check && dataSet != null) {
3603                 for (ParentForeignKeyConstraintEnumerator constraints = new ParentForeignKeyConstraintEnumerator(dataSet, this); constraints.GetNext();) {
3604                     constraints.GetForeignKeyConstraint().CheckCanRemoveParentRow(row);
3605                 }
3606             }
3607
3608             int oldRecord = row.oldRecord;
3609             int newRecord = row.newRecord;
3610
3611             DataViewRowState oldRecordStatePre = row.GetRecordState(oldRecord);
3612             DataViewRowState newRecordStatePre = row.GetRecordState(newRecord);
3613
3614             row.oldRecord = -1;
3615             row.newRecord = -1;
3616
3617             if (oldRecord == newRecord) {
3618                 oldRecord = -1;
3619             }
3620
3621             RecordStateChanged(oldRecord, oldRecordStatePre, DataViewRowState.None,
3622                                newRecord, newRecordStatePre, DataViewRowState.None);
3623
3624             FreeRecord(ref oldRecord);
3625             FreeRecord(ref newRecord);
3626
3627             row.rowID = -1;
3628             Rows.ArrayRemove(row);
3629         }
3630
3631         // Resets the table back to its original state.
3632         public virtual void Reset() {
3633             IntPtr hscp;
3634             Bid.ScopeEnter(out hscp, "<ds.DataTable.Reset|API> %d#\n", ObjectID);
3635             try {
3636                 Clear();
3637                 ResetConstraints();
3638
3639                 DataRelationCollection dr = this.ParentRelations;
3640                 int count = dr.Count;
3641                   while (count > 0) {
3642                    count--;
3643                    dr.RemoveAt(count);
3644                 }
3645
3646                 dr = this.ChildRelations;
3647                 count = dr.Count;
3648                   while (count > 0) {
3649                    count--;
3650                    dr.RemoveAt(count);
3651                 }
3652
3653                 Columns.Clear();
3654                 indexes.Clear();
3655             }
3656             finally{
3657                 Bid.ScopeLeave(ref hscp);
3658             }
3659         }
3660
3661         internal void ResetIndexes() {
3662             ResetInternalIndexes(null);
3663         }
3664
3665         internal void ResetInternalIndexes(DataColumn column) {
3666             Debug.Assert(null != indexes, "unexpected null indexes");
3667             SetShadowIndexes();
3668             try{
3669                 // the length of shadowIndexes will not change
3670                 // but the array instance may change during
3671                 // events during Index.Reset
3672                 int numIndexes = shadowIndexes.Count;
3673                 for (int i = 0; i < numIndexes; i++) {
3674                     Index ndx = shadowIndexes[i];// shadowindexes may change, see ShadowIndexCopy()
3675                     if (0 < ndx.RefCount) {
3676                         if (null == column) {
3677                             ndx.Reset();
3678                         }
3679                         else {
3680                             // SQLBU 501916: DataTable internal index is corrupted:'5'
3681                             bool found = false;
3682                             foreach(IndexField field in ndx.IndexFields) {
3683                                 if (Object.ReferenceEquals(column, field.Column)) {
3684                                     found = true;
3685                                     break;
3686                                     
3687                                 }
3688                             }
3689                             if (found) {
3690                                 ndx.Reset();
3691                             }
3692                         }
3693                     }
3694                 }
3695             }
3696             finally {
3697                 RestoreShadowIndexes();
3698             }
3699         }
3700
3701         internal void RollbackRow(DataRow row) {
3702             row.CancelEdit();
3703             SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
3704         }
3705
3706         private DataRowChangeEventArgs RaiseRowChanged(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3707             try {
3708                 if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangedDelegate))) {
3709                     args = OnRowChanged(args, eRow, eAction);
3710                 }
3711                 // check if we deleting good row
3712                 else if (DataRowAction.Delete == eAction && eRow.newRecord == -1 && (IsTypedDataTable || (null != onRowDeletedDelegate))) {
3713                     if (null == args) {
3714                         args = new DataRowChangeEventArgs(eRow, eAction);
3715                     }
3716                     OnRowDeleted(args);
3717                 }
3718             }
3719             catch (Exception f) {
3720                // 
3721                if (!Common.ADP.IsCatchableExceptionType(f)) {
3722                  throw;
3723                }
3724                ExceptionBuilder.TraceExceptionWithoutRethrow(f);
3725                // ignore the exception
3726             }
3727             return args;
3728         }
3729
3730         private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction) {
3731             if (UpdatingCurrent(eRow, eAction) && (IsTypedDataTable || (null != onRowChangingDelegate))) {
3732                 eRow.inChangingEvent = true;
3733
3734                 // don't catch
3735                 try {
3736                     args = OnRowChanging(args, eRow, eAction);
3737                 }
3738                 finally {
3739                     eRow.inChangingEvent = false;
3740                 }
3741             }
3742             // check if we deleting good row
3743             else if (DataRowAction.Delete == eAction && eRow.newRecord != -1 && (IsTypedDataTable || (null != onRowDeletingDelegate))) {
3744                 eRow.inDeletingEvent = true;
3745                 // don't catch
3746                 try {
3747                     if (null == args) {
3748                         args = new DataRowChangeEventArgs(eRow, eAction);
3749                     }
3750                     OnRowDeleting(args);
3751                 }
3752                 finally {
3753                     eRow.inDeletingEvent = false;
3754                 }
3755             }
3756             return args;
3757         }
3758
3759         private DataRowChangeEventArgs RaiseRowChanging(DataRowChangeEventArgs args, DataRow eRow, DataRowAction eAction, bool fireEvent) {
3760
3761             // check all constraints
3762             if (EnforceConstraints && !inLoad ) {
3763                 int columnCount = columnCollection.Count;
3764                 for(int i = 0; i < columnCount; ++i) {
3765                     DataColumn column = columnCollection[i];
3766                     if (!column.Computed || eAction != DataRowAction.Add) {
3767                         column.CheckColumnConstraint(eRow, eAction);
3768                     }
3769                 }
3770
3771                 int constraintCount = constraintCollection.Count;
3772                 for(int i = 0; i < constraintCount; ++i) {
3773                     constraintCollection[i].CheckConstraint(eRow, eAction);
3774                 }
3775             }
3776
3777             // $$anandra.  Check this event out. May be an issue.
3778             if (fireEvent) {
3779                 args = RaiseRowChanging(args, eRow, eAction);
3780             }
3781
3782             if (!inDataLoad) {
3783                 // cascade things...
3784                 if (!MergingData && eAction != DataRowAction.Nothing && eAction != DataRowAction.ChangeOriginal) {
3785                     CascadeAll(eRow, eAction);
3786                 }
3787             }
3788             return args;
3789         }
3790
3791         /// <devdoc>
3792         /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects.</para>
3793         /// </devdoc>
3794         public DataRow[] Select() {
3795             Bid.Trace("<ds.DataTable.Select|API> %d#\n", ObjectID);
3796             return new Select(this, "", "", DataViewRowState.CurrentRows).SelectRows();
3797         }
3798
3799         /// <devdoc>
3800         /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria in order of
3801         ///    primary key (or lacking one, order of addition.)</para>
3802         /// </devdoc>
3803         public DataRow[] Select(string filterExpression) {
3804             Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls'\n", ObjectID, filterExpression);
3805             return new Select(this, filterExpression, "", DataViewRowState.CurrentRows).SelectRows();
3806         }
3807
3808         /// <devdoc>
3809         /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter criteria, in the the
3810         ///    specified sort order.</para>
3811         /// </devdoc>
3812         public DataRow[] Select(string filterExpression, string sort) {
3813             Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls'\n", ObjectID, filterExpression, sort);
3814             return new Select(this, filterExpression, sort, DataViewRowState.CurrentRows).SelectRows();
3815         }
3816
3817         /// <devdoc>
3818         /// <para>Returns an array of all <see cref='System.Data.DataRow'/> objects that match the filter in the order of the
3819         ///    sort, that match the specified state.</para>
3820         /// </devdoc>
3821         public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates) {
3822             Bid.Trace("<ds.DataTable.Select|API> %d#, filterExpression='%ls', sort='%ls', recordStates=%d{ds.DataViewRowState}\n", ObjectID, filterExpression, sort, (int)recordStates);
3823             return new Select(this, filterExpression, sort, recordStates).SelectRows();
3824         }
3825
3826         internal void SetNewRecord(DataRow row, int proposedRecord, DataRowAction action = DataRowAction.Change, bool isInMerge = false, bool fireEvent = true, bool suppressEnsurePropertyChanged = false) {
3827             Exception deferredException = null;
3828             SetNewRecordWorker(row, proposedRecord, action, isInMerge, suppressEnsurePropertyChanged, -1, fireEvent, out deferredException); // we are going to call below overload from insert
3829             if (deferredException != null) {
3830                 throw deferredException;
3831             }
3832         }
3833
3834         private void SetNewRecordWorker(DataRow row, int proposedRecord, DataRowAction action, bool isInMerge, bool suppressEnsurePropertyChanged,
3835             int position, bool fireEvent, out Exception deferredException) {
3836
3837             // this is the event workhorse... it will throw the changing/changed events
3838             // and update the indexes. Used by change, add, delete, revert.
3839
3840             // order of execution is as follows
3841             //
3842             // 1) set temp record
3843             // 2) Check constraints for non-expression columns
3844             // 3) Raise RowChanging/RowDeleting with temp record
3845             // 4) set the new record in storage
3846             // 5) Update indexes with recordStateChanges - this will fire ListChanged & PropertyChanged events on associated views
3847             // 6) Evaluate all Expressions (exceptions are deferred)- this will fire ListChanged & PropertyChanged events on associated views
3848             // 7) Raise RowChanged/ RowDeleted
3849             // 8) Check constraints for expression columns
3850
3851             Debug.Assert(row != null, "Row can't be null.");
3852             deferredException = null;
3853             
3854             if (row.tempRecord != proposedRecord) {
3855                 // $HACK: for performance reasons, EndUpdate calls SetNewRecord with tempRecord == proposedRecord
3856                 if (!inDataLoad) {
3857                     row.CheckInTable();
3858                     CheckNotModifying(row);
3859                 }
3860                 if (proposedRecord == row.newRecord) {
3861                     if (isInMerge) {
3862                         Debug.Assert(fireEvent, "SetNewRecord is called with wrong parameter");
3863                         RaiseRowChanged(null, row, action);
3864                     }
3865                     return;
3866                 }
3867
3868                 Debug.Assert(!row.inChangingEvent, "How can this row be in an infinite loop?");
3869
3870                 row.tempRecord = proposedRecord;
3871             }
3872             DataRowChangeEventArgs drcevent = null;
3873
3874             try {
3875                 row._action = action;
3876                 drcevent = RaiseRowChanging(null, row, action, fireEvent);
3877             }
3878             catch {
3879                 row.tempRecord = -1;
3880                 throw;
3881             }
3882             finally {
3883                 row._action = DataRowAction.Nothing;
3884             }
3885
3886             row.tempRecord = -1;
3887
3888             int currentRecord = row.newRecord;
3889
3890             // if we're deleting, then the oldRecord value will change, so need to track that if it's distinct from the newRecord.
3891             int secondRecord = (proposedRecord != -1 ?
3892                                 proposedRecord :
3893                                 (row.RowState != DataRowState.Unchanged ?
3894                                  row.oldRecord :
3895                                  -1));
3896
3897             if (action == DataRowAction.Add) { //if we come here from insert we do insert the row to collection
3898                 if (position == -1)
3899                     Rows.ArrayAdd(row);
3900                 else
3901                     Rows.ArrayInsert(row, position);
3902             }
3903
3904             List<DataRow> cachedRows = null;
3905             if ((action == DataRowAction.Delete || action == DataRowAction.Change)
3906                 && dependentColumns != null && dependentColumns.Count > 0) {
3907                 // if there are expression columns, need to cache related rows for deletes and updates (key changes)
3908                 // before indexes are modified.
3909                 cachedRows = new List<DataRow>();
3910                 for (int j = 0; j < ParentRelations.Count; j++) {
3911                     DataRelation relation = ParentRelations[j];
3912                     if (relation.ChildTable != row.Table) {
3913                         continue;
3914                     }
3915                     cachedRows.InsertRange(cachedRows.Count, row.GetParentRows(relation));
3916                 }
3917
3918                 for (int j = 0; j < ChildRelations.Count; j++) {
3919                     DataRelation relation = ChildRelations[j];
3920                     if (relation.ParentTable != row.Table) {
3921                         continue;
3922                     }
3923                     cachedRows.InsertRange(cachedRows.Count, row.GetChildRows(relation));
3924                 }
3925             }
3926
3927             // Dev10 Bug 688779: DataRowView.PropertyChanged are not raised on RejectChanges
3928             // if the newRecord is changing, the propertychanged event should be allowed to triggered for ListChangedType.Changed or .Moved
3929             // unless the specific condition is known that no data has changed, like DataRow.SetModified()
3930             if (!suppressEnsurePropertyChanged && !row.HasPropertyChanged && (row.newRecord != proposedRecord)
3931                 && (-1 != proposedRecord) // explictly not fixing Dev10 Bug 692044: DataRowView.PropertyChanged are not raised on DataTable.Delete when mixing current and original records in RowStateFilter
3932                 && (-1 != row.newRecord)) // explictly not fixing parts of Dev10 Bug 697909: when mixing current and original records in RowStateFilter
3933             {
3934                 // DataRow will believe multiple edits occured and
3935                 // DataView.ListChanged event w/ ListChangedType.ItemChanged will raise DataRowView.PropertyChanged event and
3936                 // PropertyChangedEventArgs.PropertyName will now be empty string so
3937                 // WPF will refresh the entire row
3938                 row.LastChangedColumn = null;
3939                 row.LastChangedColumn = null;
3940             }
3941
3942                 // Check whether we need to update indexes
3943                 if (LiveIndexes.Count != 0) {
3944
3945                     // Dev10 bug #463087: DataTable internal index is currupted: '5'
3946                     if ((-1 == currentRecord) && (-1 != proposedRecord) && (-1 != row.oldRecord) && (proposedRecord != row.oldRecord)) {
3947                         // the transition from DataRowState.Deleted -> DataRowState.Modified
3948                         // with same orginal record but new current record
3949                         // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
3950                         // for indexes/views listening for both DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent
3951                         currentRecord = row.oldRecord;
3952                     }
3953
3954                     DataViewRowState currentRecordStatePre = row.GetRecordState(currentRecord);
3955                     DataViewRowState secondRecordStatePre = row.GetRecordState(secondRecord);
3956
3957                     row.newRecord = proposedRecord;
3958                     if (proposedRecord != -1)
3959                         this.recordManager[proposedRecord] = row;
3960
3961                     DataViewRowState currentRecordStatePost = row.GetRecordState(currentRecord);
3962                     DataViewRowState secondRecordStatePost = row.GetRecordState(secondRecord);
3963
3964                     // may raise DataView.ListChanged event
3965                     RecordStateChanged(currentRecord, currentRecordStatePre, currentRecordStatePost,
3966                         secondRecord, secondRecordStatePre, secondRecordStatePost);
3967                 }
3968                 else {
3969                     row.newRecord = proposedRecord;
3970                     if (proposedRecord != -1)
3971                         this.recordManager[proposedRecord] = row;
3972                 }
3973
3974                 // Dev10 Bug 461199 - reset the last changed column here, after all
3975                 // DataViews have raised their DataRowView.PropertyChanged event
3976                 row.ResetLastChangedColumn();
3977
3978                 // SQLBU 278737: Record manager corruption when reentrant write operations
3979                 // free the 'currentRecord' only after all the indexes have been updated.
3980                 // Corruption! { if (currentRecord != row.oldRecord) { FreeRecord(ref currentRecord); } }
3981                 // RecordStateChanged raises ListChanged event at which time user may do work
3982                 if (-1 != currentRecord) {
3983                     if (currentRecord != row.oldRecord)
3984                     {
3985                         if ((currentRecord != row.tempRecord) &&   // Delete, AcceptChanges, BeginEdit
3986                             (currentRecord != row.newRecord) &&    // RejectChanges & SetAdded
3987                             (row == recordManager[currentRecord])) // AcceptChanges, NewRow
3988                         {
3989                             FreeRecord(ref currentRecord);
3990                         }
3991                     }
3992                 }
3993
3994             if (row.RowState == DataRowState.Detached && row.rowID != -1) {
3995                 RemoveRow(row, false);
3996             }
3997
3998             if (dependentColumns != null && dependentColumns.Count > 0) {
3999                 try {
4000                     EvaluateExpressions(row, action, cachedRows);
4001                 }
4002                 catch (Exception exc) {
4003                     // For DataRows being added, throwing of exception from expression evaluation is
4004                     // deferred until after the row has been completely added.
4005                     if (action != DataRowAction.Add) {
4006                         throw exc;
4007                     }
4008                     else {
4009                         deferredException = exc;
4010                     }
4011                 }
4012             }
4013
4014             try {
4015                 if (fireEvent) {
4016                     RaiseRowChanged(drcevent, row, action);
4017                 }
4018             }
4019             catch (Exception e) {
4020                 // 
4021                 if (!Common.ADP.IsCatchableExceptionType(e)) {
4022                     throw;
4023                 }
4024                 ExceptionBuilder.TraceExceptionWithoutRethrow(e);
4025                 // ignore the exception
4026             }
4027         }
4028
4029         // this is the event workhorse... it will throw the changing/changed events
4030         // and update the indexes.
4031         internal void SetOldRecord(DataRow row, int proposedRecord) {
4032             if (!inDataLoad) {
4033                 row.CheckInTable();
4034                 CheckNotModifying(row);
4035             }
4036
4037             if (proposedRecord == row.oldRecord) {
4038                 return;
4039             }
4040
4041             int originalRecord = row.oldRecord; // cache old record after potential RowChanging event
4042             try {
4043                 // Check whether we need to update indexes
4044                 if (LiveIndexes.Count != 0) {
4045
4046                     // Dev10 bug #463087: DataTable internal index is currupted: '5'
4047                     if ((-1 == originalRecord) && (-1 != proposedRecord) && (-1 != row.newRecord) && (proposedRecord != row.newRecord)) {
4048                         // the transition from DataRowState.Added -> DataRowState.Modified
4049                         // with same current record but new original record
4050                         // needs to raise an ItemChanged or ItemMoved instead of ItemAdded in the ListChanged event.
4051                         // for indexes/views listening for both DataViewRowState.Added | DataViewRowState.ModifiedOriginal
4052                         originalRecord = row.newRecord;
4053                     }
4054
4055                     DataViewRowState originalRecordStatePre = row.GetRecordState(originalRecord);
4056                     DataViewRowState proposedRecordStatePre = row.GetRecordState(proposedRecord);
4057
4058                     row.oldRecord = proposedRecord;
4059                     if (proposedRecord != -1)
4060                         this.recordManager[proposedRecord] = row;
4061
4062                     DataViewRowState originalRecordStatePost = row.GetRecordState(originalRecord);
4063                     DataViewRowState proposedRecordStatePost = row.GetRecordState(proposedRecord);
4064
4065                     RecordStateChanged(originalRecord, originalRecordStatePre, originalRecordStatePost,
4066                                        proposedRecord, proposedRecordStatePre, proposedRecordStatePost);
4067                 }
4068                 else {
4069                     row.oldRecord = proposedRecord;
4070                     if (proposedRecord != -1)
4071                         this.recordManager[proposedRecord] = row;
4072                 }
4073             }
4074             finally {
4075                 if ((originalRecord != -1) && (originalRecord != row.tempRecord) &&
4076                     (originalRecord != row.oldRecord) && (originalRecord != row.newRecord)) {
4077
4078                     FreeRecord(ref originalRecord);
4079                 }
4080                 // else during an event 'row.AcceptChanges(); row.BeginEdit(); row.EndEdit();'
4081
4082                 if (row.RowState == DataRowState.Detached && row.rowID != -1) {
4083                     RemoveRow(row, false);
4084                 }
4085             }
4086         }
4087
4088         private void RestoreShadowIndexes() {
4089             Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
4090             shadowCount--;
4091             if (0 == shadowCount) {
4092                 shadowIndexes = null;
4093             }
4094         }
4095
4096         private void SetShadowIndexes() {
4097             if (null == shadowIndexes) {
4098                 Debug.Assert(0 == shadowCount, "unexpected count");
4099                 shadowIndexes = LiveIndexes;
4100                 shadowCount = 1;
4101             }
4102             else {
4103                 Debug.Assert(1 <= shadowCount, "unexpected negative shadow count");
4104                 shadowCount++;
4105             }
4106         }
4107
4108         internal void ShadowIndexCopy(){
4109             if (shadowIndexes == indexes) {
4110                 Debug.Assert(0 < indexes.Count, "unexpected");
4111                 shadowIndexes = new List<Index>(indexes);
4112             }
4113         }
4114
4115         /// <devdoc>
4116         /// <para>Returns the <see cref='System.Data.DataTable.TableName'/> and <see cref='System.Data.DataTable.DisplayExpression'/>, if there is one as a concatenated string.</para>
4117         /// </devdoc>
4118         public override string ToString() {
4119             if (this.displayExpression == null)
4120                 return this.TableName;
4121             else
4122                 return this.TableName + " + " + this.DisplayExpressionInternal;
4123         }
4124
4125         /// <devdoc>
4126         ///    <para>[To be supplied.]</para>
4127         /// </devdoc>
4128         public void BeginLoadData() {
4129             IntPtr hscp;
4130             Bid.ScopeEnter(out hscp, "<ds.DataTable.BeginLoadData|API> %d#\n", ObjectID);
4131             try {
4132                 if (inDataLoad)
4133                     return;
4134
4135                 inDataLoad = true;
4136                 Debug.Assert(null == loadIndex, "loadIndex should already be null");
4137                 loadIndex  = null;
4138                 // LoadDataRow may have been called before BeginLoadData and already
4139                 // initialized loadIndexwithOriginalAdded & loadIndexwithCurrentDeleted 
4140
4141                 initialLoad = (Rows.Count == 0);
4142                 if(initialLoad) {
4143                     SuspendIndexEvents();
4144                 } else {
4145                     if (primaryKey != null) {
4146                         loadIndex = primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
4147                     }
4148                     if(loadIndex != null) {
4149                         loadIndex.AddRef();
4150                     }
4151                 }
4152
4153                 if (DataSet != null) {
4154                     savedEnforceConstraints = DataSet.EnforceConstraints;
4155                     DataSet.EnforceConstraints = false;
4156                 }
4157                 else {
4158                     this.EnforceConstraints = false;
4159                 }
4160             }
4161             finally{
4162                 Bid.ScopeLeave(ref hscp);
4163             }
4164         }
4165
4166         /// <devdoc>
4167         ///    <para>[To be supplied.]</para>
4168         /// </devdoc>
4169         public void EndLoadData() {
4170             IntPtr hscp;
4171             Bid.ScopeEnter(out hscp, "<ds.DataTable.EndLoadData|API> %d#\n", ObjectID);
4172             try {
4173                 if (!inDataLoad)
4174                     return;
4175
4176                 if(loadIndex != null) {
4177                     loadIndex.RemoveRef();
4178                 }
4179                 if (loadIndexwithOriginalAdded  != null) {
4180                     loadIndexwithOriginalAdded.RemoveRef();
4181                 }
4182                 if (loadIndexwithCurrentDeleted  != null) {
4183                     loadIndexwithCurrentDeleted.RemoveRef();
4184                 }
4185
4186                 loadIndex  = null;
4187                 loadIndexwithOriginalAdded = null;
4188                 loadIndexwithCurrentDeleted = null;
4189
4190                 inDataLoad = false;
4191
4192                 RestoreIndexEvents(false);
4193
4194                 if (DataSet != null)
4195                     DataSet.EnforceConstraints = savedEnforceConstraints;
4196                 else
4197                     this.EnforceConstraints = true;
4198             }
4199             finally{
4200                 Bid.ScopeLeave(ref hscp);
4201             }
4202         }
4203
4204         /// <devdoc>
4205         ///    <para>Finds and updates a specific row. If no matching
4206         ///       row is found, a new row is created using the given values.</para>
4207         /// </devdoc>
4208         public DataRow LoadDataRow(object[] values, bool fAcceptChanges) {
4209             IntPtr hscp;
4210             Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, fAcceptChanges=%d{bool}\n", ObjectID, fAcceptChanges);
4211             try {
4212                 DataRow row;
4213                 if (inDataLoad) {
4214                     int record = NewRecordFromArray(values);
4215                     if (loadIndex != null) {
4216                         // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
4217                         Debug.Assert(2 <= loadIndex.RefCount, "bad loadIndex.RefCount");
4218
4219                         int result = loadIndex.FindRecord(record);
4220                         if (result != -1) {
4221                             int resultRecord = loadIndex.GetRecord(result);
4222                             row = recordManager[resultRecord];
4223                             Debug.Assert (row != null, "Row can't be null for index record");
4224                             row.CancelEdit();
4225                             if (row.RowState == DataRowState.Deleted)
4226                                 SetNewRecord(row, row.oldRecord, DataRowAction.Rollback, false, true);
4227                             SetNewRecord(row, record, DataRowAction.Change, false, true);
4228                             if (fAcceptChanges)
4229                                 row.AcceptChanges();
4230                             return row;
4231                         }
4232                     }
4233                     row = NewRow(record);
4234                     AddRow(row);
4235                     if (fAcceptChanges)
4236                         row.AcceptChanges();
4237                     return row;
4238                 }
4239                 else {
4240                     // In case, BeginDataLoad is not called yet
4241                     row = UpdatingAdd(values);
4242                     if (fAcceptChanges)
4243                         row.AcceptChanges();
4244                     return row;
4245                 }
4246             }
4247             finally{
4248                 Bid.ScopeLeave(ref hscp);
4249             }
4250         }
4251
4252         /// <devdoc>
4253         ///    <para>Finds and updates a specific row. If no matching
4254         ///       row is found, a new row is created using the given values.</para>
4255         /// </devdoc>
4256         public DataRow LoadDataRow(object[] values, LoadOption loadOption) {
4257             IntPtr hscp;
4258             Bid.ScopeEnter(out hscp, "<ds.DataTable.LoadDataRow|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID,  (int)loadOption);
4259             try {
4260                 Index indextoUse = null;
4261                 if (this.primaryKey != null) {
4262                     if (loadOption == LoadOption.Upsert) { // CurrentVersion, and Deleted
4263                         if (loadIndexwithCurrentDeleted == null) {
4264                             loadIndexwithCurrentDeleted = this.primaryKey.Key.GetSortIndex(DataViewRowState.CurrentRows |DataViewRowState.Deleted);
4265                             Debug.Assert(loadIndexwithCurrentDeleted != null, "loadIndexwithCurrentDeleted should not be null" );
4266                             if (loadIndexwithCurrentDeleted != null) {
4267                                 loadIndexwithCurrentDeleted.AddRef();
4268                             }
4269                         }
4270                         indextoUse = loadIndexwithCurrentDeleted;
4271                     }
4272                     else {// CurrentVersion, and Deleted : OverwriteRow, PreserveCurrentValues
4273                         if (loadIndexwithOriginalAdded == null) {
4274                             loadIndexwithOriginalAdded  = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows |DataViewRowState.Added);
4275                             Debug.Assert(loadIndexwithOriginalAdded != null, "loadIndexwithOriginalAdded should not be null");
4276                             if (loadIndexwithOriginalAdded != null) {
4277                                 loadIndexwithOriginalAdded.AddRef();
4278                             }
4279                         }
4280                         indextoUse = loadIndexwithOriginalAdded;
4281                     }
4282                     // not expecting LiveIndexes to clear the index we use between calls to LoadDataRow
4283                     Debug.Assert(2 <= indextoUse.RefCount, "bad indextoUse.RefCount");
4284                 }
4285                 if(inDataLoad && !AreIndexEventsSuspended) { // we do not want to fire any listchanged in new Load/Fill
4286                     SuspendIndexEvents();// so suspend events here(not suspended == table already has some rows initially)
4287                 }
4288
4289                 DataRow dataRow = LoadRow(values, loadOption, indextoUse);// if indextoUse == null, it means we dont have PK,
4290                                                                           // so LoadRow will take care of just adding the row to end
4291
4292                 return dataRow;
4293             }
4294             finally {
4295                 Bid.ScopeLeave(ref hscp);
4296             }
4297         }
4298
4299         internal DataRow UpdatingAdd(object[] values) {
4300             Index index = null;
4301             if (this.primaryKey != null) {
4302                 index = this.primaryKey.Key.GetSortIndex(DataViewRowState.OriginalRows);
4303             }
4304
4305             if (index != null) {
4306                 int record = NewRecordFromArray(values);
4307                 int result = index.FindRecord(record);
4308                 if (result != -1) {
4309                     int resultRecord = index.GetRecord(result);
4310                     DataRow row = this.recordManager[resultRecord];
4311                     Debug.Assert (row != null, "Row can't be null for index record");
4312                     row.RejectChanges();
4313                     this.SetNewRecord(row, record);
4314                     return row;
4315                 }
4316                 DataRow row2 = NewRow(record);
4317                 Rows.Add(row2);
4318                 return row2;
4319             }
4320
4321             return Rows.Add(values);
4322         }
4323
4324         internal bool UpdatingCurrent(DataRow row, DataRowAction action) {
4325             return(action == DataRowAction.Add || action == DataRowAction.Change ||
4326                    action == DataRowAction.Rollback || action == DataRowAction.ChangeOriginal ||
4327                    action == DataRowAction.ChangeCurrentAndOriginal);
4328 //                (action == DataRowAction.Rollback && row.tempRecord != -1));
4329 }
4330
4331         internal DataColumn AddUniqueKey(int position) {
4332             if (_colUnique != null)
4333                 return _colUnique;
4334
4335             // check to see if we can use already existant PrimaryKey
4336             DataColumn[] pkey = PrimaryKey;
4337             if (pkey.Length == 1)
4338                 // We have one-column primary key, so we can use it in our heirarchical relation
4339                 return pkey[0];
4340
4341             // add Unique, but not primaryKey to the table
4342
4343             string keyName = XMLSchema.GenUniqueColumnName(TableName + "_Id", this);
4344             DataColumn key = new DataColumn(keyName, typeof(Int32), null, MappingType.Hidden);
4345             key.Prefix = tablePrefix;
4346             key.AutoIncrement = true;
4347             key.AllowDBNull = false;
4348             key.Unique = true;
4349
4350             if (position == -1)
4351                 Columns.Add(key);
4352             else { // we do have a problem and Imy idea is it is bug. Ask Enzo while Code review. Why we do not set ordinal when we call AddAt?
4353                 for(int i = Columns.Count -1; i >= position; i--) {
4354                     this.Columns[i].SetOrdinalInternal(i+1);
4355                 }
4356                 Columns.AddAt(position, key);
4357                 key.SetOrdinalInternal(position);
4358             }
4359
4360             if (pkey.Length == 0)
4361                 PrimaryKey = new DataColumn[] {
4362                     key
4363                 };
4364
4365             _colUnique = key;
4366             return _colUnique;
4367         }
4368
4369         internal DataColumn AddUniqueKey() {
4370             return AddUniqueKey(-1);
4371         }
4372
4373         internal DataColumn AddForeignKey(DataColumn parentKey) {
4374             Debug.Assert(parentKey != null, "AddForeignKey: Invalid paramter.. related primary key is null");
4375
4376             string      keyName = XMLSchema.GenUniqueColumnName(parentKey.ColumnName, this);
4377             DataColumn  foreignKey = new DataColumn(keyName, parentKey.DataType, null, MappingType.Hidden);
4378             Columns.Add(foreignKey);
4379
4380             return foreignKey;
4381         }
4382
4383         internal void UpdatePropertyDescriptorCollectionCache() {
4384             propertyDescriptorCollectionCache = null;
4385         }
4386
4387         /// <devdoc>
4388         ///     Retrieves an array of properties that the given component instance
4389         ///     provides.  This may differ from the set of properties the class
4390         ///     provides.  If the component is sited, the site may add or remove
4391         ///     additional properties.  The returned array of properties will be
4392         ///     filtered by the given set of attributes.
4393         /// </devdoc>
4394         internal PropertyDescriptorCollection GetPropertyDescriptorCollection(Attribute[] attributes) {
4395             if (propertyDescriptorCollectionCache == null) {
4396                 int columnsCount   = Columns.Count;
4397                 int relationsCount = ChildRelations.Count;
4398                 PropertyDescriptor[] props = new PropertyDescriptor[columnsCount + relationsCount]; {
4399                     for (int i = 0; i < columnsCount; i++) {
4400                         props[i] = new DataColumnPropertyDescriptor(Columns[i]);
4401                     }
4402                     for (int i = 0; i < relationsCount; i++) {
4403                         props[columnsCount + i] = new DataRelationPropertyDescriptor(ChildRelations[i]);
4404                     }
4405                 }
4406                 propertyDescriptorCollectionCache = new PropertyDescriptorCollection(props);
4407             }
4408             return propertyDescriptorCollectionCache;
4409         }
4410
4411         internal XmlQualifiedName TypeName {
4412             get {
4413                 return ((typeName == null) ? XmlQualifiedName.Empty : (XmlQualifiedName)typeName);
4414             }
4415             set {
4416                 typeName = value;
4417             }
4418         }
4419
4420         public void Merge(DataTable table)
4421         {
4422             Merge(table, false, MissingSchemaAction.Add);
4423         }
4424
4425         public void Merge(DataTable table, bool preserveChanges)
4426         {
4427             Merge(table, preserveChanges, MissingSchemaAction.Add);
4428         }
4429
4430         public void Merge(DataTable table, bool preserveChanges, MissingSchemaAction missingSchemaAction)
4431         {
4432             IntPtr hscp;
4433             Bid.ScopeEnter(out hscp, "<ds.DataTable.Merge|API> %d#, table=%d, preserveChanges=%d{bool}, missingSchemaAction=%d{ds.MissingSchemaAction}\n", ObjectID, (table != null) ? table.ObjectID : 0, preserveChanges, (int)missingSchemaAction);
4434             try{
4435                 if (table == null)
4436                     throw ExceptionBuilder.ArgumentNull("table");
4437
4438                 switch(missingSchemaAction) { // @perfnote: Enum.IsDefined
4439                 case MissingSchemaAction.Add:
4440                 case MissingSchemaAction.Ignore:
4441                 case MissingSchemaAction.Error:
4442                 case MissingSchemaAction.AddWithKey:
4443                     Merger merger = new Merger(this, preserveChanges, missingSchemaAction);
4444                     merger.MergeTable(table);
4445                     break;
4446                 default:
4447                     throw Common.ADP.InvalidMissingSchemaAction(missingSchemaAction);
4448                 }
4449             }
4450             finally{
4451                 Bid.ScopeLeave(ref hscp);
4452             }
4453         }
4454
4455         public void Load (IDataReader reader){
4456             Load(reader, LoadOption.PreserveChanges, null);
4457         }
4458
4459         public void Load (IDataReader reader, LoadOption loadOption) {
4460             Load(reader, loadOption, null);
4461         }
4462
4463         public virtual void Load (IDataReader reader, LoadOption loadOption, FillErrorEventHandler errorHandler){
4464             IntPtr hscp;
4465             Bid.ScopeEnter(out hscp, "<ds.DataTable.Load|API> %d#, loadOption=%d{ds.LoadOption}\n", ObjectID, (int)loadOption);
4466             try {
4467                 if (this.PrimaryKey.Length == 0) {
4468                     DataTableReader dtReader = reader as DataTableReader;
4469                     if (dtReader != null && dtReader.CurrentDataTable == this)
4470                         return; // if not return, it will go to infinite loop
4471                 }
4472                 Common.LoadAdapter adapter = new Common.LoadAdapter();
4473                 adapter.FillLoadOption = loadOption;
4474                 adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
4475                 if (null != errorHandler) {
4476                     adapter.FillError += errorHandler;
4477                 }
4478                 adapter.FillFromReader(new DataTable[] { this }, reader, 0, 0);
4479
4480                 if (!reader.IsClosed && !reader.NextResult()) { // 
4481                     reader.Close();
4482                 }
4483             }
4484             finally {
4485                 Bid.ScopeLeave(ref hscp);
4486             }
4487         }
4488
4489         private DataRow LoadRow(object[] values, LoadOption loadOption, Index searchIndex) {
4490             int recordNo;
4491             DataRow dataRow = null;
4492
4493             if (searchIndex != null) {
4494                 int[] primaryKeyIndex = new int[0];
4495                 if (this.primaryKey != null) { // I do check above for PK, but in case if someone else gives me some index unrelated to PK
4496                     primaryKeyIndex = new int[this.primaryKey.ColumnsReference.Length];
4497                     for(int i = 0; i < this.primaryKey.ColumnsReference.Length; i++) {
4498                         primaryKeyIndex[i] = this.primaryKey.ColumnsReference[i].Ordinal;
4499                     }
4500                 }
4501
4502                 object[] keys = new object[primaryKeyIndex.Length];
4503                 for(int i = 0; i < primaryKeyIndex.Length; i++) {
4504                     keys[i] = values[primaryKeyIndex[i]];
4505                 }
4506
4507                 Range result = searchIndex.FindRecords(keys);
4508
4509                 if (!result.IsNull) {
4510                     int deletedRowUpsertCount = 0;
4511                     for(int i = result.Min; i <= result.Max; i++) {
4512                         int resultRecord = searchIndex.GetRecord(i);
4513                         dataRow = this.recordManager[resultRecord];
4514                         recordNo = NewRecordFromArray(values);
4515
4516                         //SQLBU DT 33648
4517                         // values array is being reused by DataAdapter, do not modify the values array
4518                         for(int count = 0; count < values.Length; count++) {
4519                             if (null == values[count]) {
4520                                 columnCollection[count].Copy(resultRecord, recordNo);
4521                             }
4522                         }
4523                         for(int count = values.Length; count < columnCollection.Count ; count++) {
4524                             columnCollection[count].Copy(resultRecord, recordNo); // if there are missing values
4525                         }
4526
4527                         if (loadOption != LoadOption.Upsert || dataRow.RowState != DataRowState.Deleted) {
4528                             SetDataRowWithLoadOption(dataRow , recordNo, loadOption, true);
4529                         }
4530                         else {
4531                             deletedRowUpsertCount++;
4532                         }
4533                     }
4534                     if (0 == deletedRowUpsertCount) {
4535                         return dataRow;
4536                     }
4537                 }
4538             }
4539
4540             recordNo = NewRecordFromArray(values);
4541             dataRow = NewRow(recordNo);
4542             // fire rowChanging event here
4543             DataRowAction action;
4544             DataRowChangeEventArgs drcevent = null;
4545             switch(loadOption) {
4546                 case LoadOption.OverwriteChanges:
4547                 case LoadOption.PreserveChanges:
4548                     action = DataRowAction.ChangeCurrentAndOriginal;
4549                     break;
4550                 case LoadOption.Upsert:
4551                     action = DataRowAction.Add;
4552                     break;
4553                 default:
4554                     throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4555             }
4556
4557             drcevent = RaiseRowChanging(null, dataRow, action);
4558
4559             this.InsertRow (dataRow, -1, -1, false);
4560             switch(loadOption) {
4561                 case LoadOption.OverwriteChanges:
4562                 case LoadOption.PreserveChanges:
4563                     this.SetOldRecord(dataRow,  recordNo);
4564                     break;
4565                 case LoadOption.Upsert:
4566                     break;
4567                 default:
4568                     throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4569             }
4570             RaiseRowChanged(drcevent, dataRow, action);
4571
4572             return dataRow;
4573         }
4574
4575         private void SetDataRowWithLoadOption (DataRow dataRow, int recordNo, LoadOption loadOption, bool checkReadOnly) {
4576             bool hasError = false;
4577             if (checkReadOnly) {
4578                 foreach(DataColumn dc in this.Columns) {
4579                     if (dc.ReadOnly && !dc.Computed) {
4580                         switch(loadOption) {
4581                             case LoadOption.OverwriteChanges:
4582                                 if ((dataRow[dc, DataRowVersion.Current] != dc[recordNo]) ||(dataRow[dc, DataRowVersion.Original] != dc[recordNo]))
4583                                     hasError = true;
4584                                 break;
4585                             case LoadOption.Upsert:
4586                                 if (dataRow[dc, DataRowVersion.Current] != dc[recordNo])
4587                                     hasError = true;
4588                                 break;
4589                             case LoadOption.PreserveChanges:
4590                                 if (dataRow[dc, DataRowVersion.Original] != dc[recordNo])
4591                                     hasError = true;
4592                                 break;
4593                         }
4594                     }
4595                 }
4596             } // No Event should be fired  in SenNewRecord and SetOldRecord
4597             // fire rowChanging event here
4598
4599             DataRowChangeEventArgs drcevent = null;
4600             DataRowAction action = DataRowAction.Nothing;
4601             int cacheTempRecord = dataRow.tempRecord;
4602             dataRow.tempRecord = recordNo;
4603
4604             switch(loadOption) {
4605                 case LoadOption.OverwriteChanges:
4606                     action = DataRowAction.ChangeCurrentAndOriginal;
4607                     break;
4608                 case LoadOption.Upsert:
4609                     switch(dataRow.RowState) {
4610                         case DataRowState.Unchanged:
4611                             // let see if the incomming value has the same values as existing row, so compare records
4612                             foreach(DataColumn dc in dataRow.Table.Columns) {
4613                                 if (0 != dc.Compare(dataRow.newRecord, recordNo)) {
4614                                     action = DataRowAction.Change;
4615                                     break;
4616                                 }
4617                             }
4618                             break;
4619                         case DataRowState.Deleted:
4620                             Debug.Assert(false, "LoadOption.Upsert with deleted row, should not be here");
4621                             break;
4622                         default :
4623                             action = DataRowAction.Change;
4624                             break;
4625                     }
4626                     break;
4627                 case LoadOption.PreserveChanges:
4628                     switch(dataRow.RowState) {
4629                         case DataRowState.Unchanged:
4630                             action = DataRowAction.ChangeCurrentAndOriginal;
4631                             break;
4632                         default:
4633                             action = DataRowAction.ChangeOriginal;
4634                             break;
4635                     }
4636                     break;
4637                 default:
4638                     throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4639             }
4640
4641             try {
4642                 drcevent = RaiseRowChanging(null, dataRow, action);
4643                 if (action == DataRowAction.Nothing) { // RaiseRowChanging does not fire for DataRowAction.Nothing
4644                     dataRow.inChangingEvent = true;
4645                     try {
4646                         drcevent = OnRowChanging(drcevent, dataRow, action);
4647                     }
4648                     finally {
4649                         dataRow.inChangingEvent = false;
4650                     }
4651                 }
4652             }
4653             finally {
4654                 Debug.Assert(dataRow.tempRecord == recordNo, "tempRecord has been changed in event handler");
4655                 if (DataRowState.Detached == dataRow.RowState) {
4656                     // 'row.Table.Remove(row);'
4657                     if (-1 != cacheTempRecord) {
4658                         FreeRecord(ref cacheTempRecord);
4659                     }
4660                 }
4661                 else {
4662                     if (dataRow.tempRecord != recordNo) {
4663                         // 'row.EndEdit(); row.BeginEdit(); '
4664                         if (-1 != cacheTempRecord) {
4665                             FreeRecord(ref cacheTempRecord);
4666                         }
4667                         if (-1 != recordNo) {
4668                             FreeRecord(ref recordNo);
4669                         }
4670                         recordNo = dataRow.tempRecord;
4671                     }
4672                     else {
4673                         dataRow.tempRecord = cacheTempRecord;
4674                     }
4675                 }
4676             }
4677             if (dataRow.tempRecord != -1) {
4678                 dataRow.CancelEdit();
4679             }
4680
4681             switch(loadOption) {
4682                 case LoadOption.OverwriteChanges:
4683                      this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
4684                      this.SetOldRecord(dataRow,  recordNo);
4685                      break;
4686                 case LoadOption.Upsert:
4687                      if (dataRow.RowState == DataRowState.Unchanged) {
4688                          this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
4689                          if (!dataRow.HasChanges()) {
4690                              this.SetOldRecord(dataRow, recordNo);
4691                          }
4692                      }
4693                      else {
4694                          if (dataRow.RowState == DataRowState.Deleted)
4695                              dataRow.RejectChanges();
4696                          this.SetNewRecord(dataRow,  recordNo, DataRowAction.Change, false, false);
4697                      }
4698                      break;
4699                 case LoadOption.PreserveChanges:
4700                      if (dataRow.RowState == DataRowState.Unchanged) {
4701                          // SQLBU 500706: DataTable internal index is corrupted: '8'
4702                          // if ListChanged event deletes dataRow
4703                          this.SetOldRecord(dataRow,  recordNo); // do not fire event
4704                          this.SetNewRecord(dataRow, recordNo, DataRowAction.Change, false, false);
4705                      }
4706                      else { // if modified/ added / deleted we want this operation to fire event (just for LoadOption.PreserveCurrentValues)
4707                         this.SetOldRecord(dataRow,  recordNo);
4708                      }
4709                      break;
4710                 default:
4711                     throw ExceptionBuilder.ArgumentOutOfRange("LoadOption");
4712             }
4713
4714             if (hasError) {
4715                 string error = Res.GetString(Res.Load_ReadOnlyDataModified);
4716                 if (dataRow.RowError.Length == 0) { // WebData 112272, append the row error
4717                     dataRow.RowError = error;
4718                 }
4719                 else {
4720                     dataRow.RowError += " ]:[ " + error ;
4721                 }
4722
4723                 foreach(DataColumn dc in this.Columns) {
4724                     if (dc.ReadOnly && !dc.Computed)
4725                         dataRow.SetColumnError(dc, error);
4726                 }
4727             }
4728
4729             drcevent = RaiseRowChanged(drcevent, dataRow, action);
4730             if (action == DataRowAction.Nothing) { // RaiseRowChanged does not fire for DataRowAction.Nothing
4731                 dataRow.inChangingEvent = true;
4732                 try {
4733                     OnRowChanged(drcevent, dataRow, action);
4734                 }
4735                 finally {
4736                     dataRow.inChangingEvent = false;
4737                 }
4738             }
4739
4740         }
4741
4742         public  DataTableReader CreateDataReader() {
4743             return new DataTableReader(this);
4744         }
4745
4746
4747         public void WriteXml(Stream stream)
4748         {
4749             WriteXml(stream, XmlWriteMode.IgnoreSchema, false);
4750         }
4751
4752         public void WriteXml(Stream stream, bool writeHierarchy)
4753         {
4754             WriteXml(stream, XmlWriteMode.IgnoreSchema, writeHierarchy);
4755         }
4756
4757         public void WriteXml(TextWriter writer)
4758         {
4759             WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
4760         }
4761
4762         public void WriteXml(TextWriter writer, bool writeHierarchy)
4763         {
4764             WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
4765         }
4766
4767         public void WriteXml(XmlWriter writer)
4768         {
4769             WriteXml(writer, XmlWriteMode.IgnoreSchema, false);
4770         }
4771
4772         public void WriteXml(XmlWriter writer, bool writeHierarchy)
4773         {
4774             WriteXml(writer, XmlWriteMode.IgnoreSchema, writeHierarchy);
4775         }
4776
4777         [ResourceExposure(ResourceScope.Machine)]
4778         [ResourceConsumption(ResourceScope.Machine)]
4779         public void WriteXml(String fileName)
4780         {
4781             WriteXml(fileName, XmlWriteMode.IgnoreSchema, false);
4782         }
4783
4784         [ResourceExposure(ResourceScope.Machine)]
4785         [ResourceConsumption(ResourceScope.Machine)]
4786         public void WriteXml(String fileName, bool writeHierarchy)
4787         {
4788             WriteXml(fileName, XmlWriteMode.IgnoreSchema, writeHierarchy);
4789         }
4790
4791         public void WriteXml(Stream stream, XmlWriteMode mode)
4792         {
4793             WriteXml(stream, mode, false);
4794         }
4795
4796         public void WriteXml(Stream stream, XmlWriteMode mode, bool writeHierarchy)
4797         {
4798             if (stream != null) {
4799                 XmlTextWriter w =  new XmlTextWriter(stream, null) ;
4800                 w.Formatting = Formatting.Indented;
4801
4802                 WriteXml( w, mode, writeHierarchy);
4803             }
4804         }
4805
4806         public void WriteXml(TextWriter writer, XmlWriteMode mode)
4807         {
4808             WriteXml(writer, mode, false);
4809         }
4810
4811         public void WriteXml(TextWriter writer, XmlWriteMode mode, bool writeHierarchy)
4812         {
4813             if (writer != null) {
4814                 XmlTextWriter w =  new XmlTextWriter(writer) ;
4815                 w.Formatting = Formatting.Indented;
4816
4817                 WriteXml(w, mode, writeHierarchy);
4818             }
4819         }
4820
4821         public void WriteXml(XmlWriter writer, XmlWriteMode mode)
4822         {
4823             WriteXml(writer, mode, false);
4824         }
4825         public void WriteXml(XmlWriter writer, XmlWriteMode mode, bool writeHierarchy)
4826         {
4827             IntPtr hscp;
4828             Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, mode=%d{ds.XmlWriteMode}\n", ObjectID,  (int)mode);
4829             try{
4830                 if (this.tableName.Length == 0) {
4831                     throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
4832                 }
4833                 // Generate SchemaTree and write it out
4834                 if (writer != null) {
4835
4836                     if (mode == XmlWriteMode.DiffGram) { // FIX THIS
4837                         // Create and save the updates
4838                         new NewDiffgramGen(this, writeHierarchy).Save(writer, this);
4839                     }
4840                     else {
4841                         // Create and save xml data
4842                         if (mode == XmlWriteMode.WriteSchema) {
4843                             DataSet ds = null;
4844                             string tablenamespace = this.tableNamespace;
4845                             if (null == this.DataSet) {
4846                                 ds = new DataSet();
4847                                 // if user set values on DataTable, it isn't necessary
4848                                 // to set them on the DataSet because they won't be inherited
4849                                 // but it is simpler to set them in both places
4850
4851                                 // if user did not set values on DataTable, it is required
4852                                 // to set them on the DataSet so the table will inherit
4853                                 // the value already on the Datatable
4854                                 ds.SetLocaleValue(_culture, _cultureUserSet);
4855                                 ds.CaseSensitive = this.CaseSensitive;
4856                                 ds.Namespace = this.Namespace;
4857                                 ds.RemotingFormat = this.RemotingFormat;
4858                                 ds.Tables.Add(this);
4859                             }
4860
4861                             if (writer != null) {
4862                                 XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
4863                                 xmldataWriter.Save(writer, /*mode == XmlWriteMode.WriteSchema*/true);
4864                             }
4865                             if (null != ds) {
4866                                 ds.Tables.Remove(this);
4867                                 this.tableNamespace = tablenamespace;
4868                             }
4869                         }
4870                         else {
4871                             XmlDataTreeWriter xmldataWriter = new XmlDataTreeWriter(this, writeHierarchy);
4872                             xmldataWriter.Save(writer,/*mode == XmlWriteMode.WriteSchema*/ false);
4873                         }
4874                     }
4875                 }
4876             }
4877             finally {
4878                 Bid.ScopeLeave(ref hscp);
4879             }
4880         }
4881
4882         [ResourceExposure(ResourceScope.Machine)]
4883         [ResourceConsumption(ResourceScope.Machine)]
4884         public void WriteXml(String fileName, XmlWriteMode mode)
4885         {
4886             WriteXml(fileName, mode, false);
4887         }
4888
4889         [ResourceExposure(ResourceScope.Machine)]
4890         [ResourceConsumption(ResourceScope.Machine)]
4891         public void WriteXml(String fileName, XmlWriteMode mode, bool writeHierarchy)
4892         {
4893             IntPtr hscp;
4894             Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXml|API> %d#, fileName='%ls', mode=%d{ds.XmlWriteMode}\n", ObjectID, fileName, (int)mode);
4895             try {
4896                 using(XmlTextWriter xw = new XmlTextWriter( fileName, null )) {
4897                     xw.Formatting = Formatting.Indented;
4898                     xw.WriteStartDocument(true);
4899
4900                     WriteXml(xw, mode, writeHierarchy);
4901
4902                     xw.WriteEndDocument();
4903                 }
4904             }
4905             finally {
4906                 Bid.ScopeLeave(ref hscp);
4907             }
4908         }
4909
4910         public void WriteXmlSchema(Stream stream) {
4911             WriteXmlSchema(stream, false);
4912         }
4913
4914         public void WriteXmlSchema(Stream stream, bool writeHierarchy)
4915         {
4916             if (stream == null)
4917                 return;
4918
4919             XmlTextWriter w =  new XmlTextWriter(stream, null) ;
4920             w.Formatting = Formatting.Indented;
4921
4922             WriteXmlSchema( w, writeHierarchy );
4923         }
4924
4925         public void WriteXmlSchema( TextWriter writer ) {
4926             WriteXmlSchema( writer, false );
4927         }
4928
4929         public void WriteXmlSchema( TextWriter writer, bool writeHierarchy )
4930         {
4931
4932             if (writer == null)
4933                 return;
4934
4935             XmlTextWriter w =  new XmlTextWriter(writer);
4936             w.Formatting = Formatting.Indented;
4937
4938             WriteXmlSchema( w, writeHierarchy );
4939         }
4940
4941         private  bool CheckForClosureOnExpressions(DataTable dt, bool writeHierarchy) {
4942             List<DataTable> tableList = new List<DataTable>();
4943             tableList.Add(dt);
4944             if (writeHierarchy) { // WebData 112161
4945                 CreateTableList(dt, tableList);
4946             }
4947             return CheckForClosureOnExpressionTables(tableList);
4948         }
4949
4950         private bool CheckForClosureOnExpressionTables(List<DataTable> tableList) {
4951             Debug.Assert(tableList != null, "tableList shouldnot be null");
4952
4953             foreach(DataTable datatable in tableList) {
4954                 foreach(DataColumn dc in datatable.Columns) {
4955                     if (dc.Expression.Length != 0)  {
4956                         DataColumn[] dependency = dc.DataExpression.GetDependency();
4957                         for (int j = 0; j < dependency.Length; j++) {
4958                             if (!(tableList.Contains(dependency[j].Table))) {
4959                                 return false;
4960                             }
4961                         }
4962                     }
4963                 }
4964             }
4965             return true;
4966         }
4967
4968
4969         public void WriteXmlSchema(XmlWriter writer) {
4970             WriteXmlSchema(writer, false);
4971         }
4972
4973         public void WriteXmlSchema(XmlWriter writer, bool writeHierarchy)
4974         {
4975             IntPtr hscp;
4976             Bid.ScopeEnter(out hscp, "<ds.DataTable.WriteXmlSchema|API> %d#\n", ObjectID);
4977             try{
4978                 if (this.tableName.Length == 0) {
4979                     throw ExceptionBuilder.CanNotSerializeDataTableWithEmptyName();
4980                 }
4981
4982                 if (!CheckForClosureOnExpressions(this, writeHierarchy)) {
4983                     throw ExceptionBuilder.CanNotSerializeDataTableHierarchy();
4984                 }
4985
4986                 DataSet ds = null;
4987                 string tablenamespace = this.tableNamespace;//SQL BU Defect Tracking 286968
4988
4989                 // Generate SchemaTree and write it out
4990                 if (null == this.DataSet) {
4991                     ds = new DataSet();
4992                     // if user set values on DataTable, it isn't necessary
4993                     // to set them on the DataSet because they won't be inherited
4994                     // but it is simpler to set them in both places
4995
4996                     // if user did not set values on DataTable, it is required
4997                     // to set them on the DataSet so the table will inherit
4998                     // the value already on the Datatable
4999                     ds.SetLocaleValue(_culture, _cultureUserSet);
5000                     ds.CaseSensitive = this.CaseSensitive;
5001                     ds.Namespace = this.Namespace;
5002                     ds.RemotingFormat = this.RemotingFormat;
5003                     ds.Tables.Add(this);
5004                 }
5005
5006                 if (writer != null) {
5007                     XmlTreeGen treeGen = new XmlTreeGen(SchemaFormat.Public);
5008                     treeGen.Save(null, this, writer, writeHierarchy);
5009                 }
5010                 if (null != ds) {
5011                     ds.Tables.Remove(this);
5012                     this.tableNamespace = tablenamespace;
5013                 }
5014             }
5015             finally {
5016                 Bid.ScopeLeave(ref hscp);
5017             }
5018         }
5019
5020         [ResourceExposure(ResourceScope.Machine)]
5021         [ResourceConsumption(ResourceScope.Machine)]
5022         public void WriteXmlSchema(String fileName) {
5023             WriteXmlSchema(fileName, false);
5024         }
5025
5026         [ResourceExposure(ResourceScope.Machine)]
5027         [ResourceConsumption(ResourceScope.Machine)]
5028         public void WriteXmlSchema(String fileName, bool writeHierarchy)
5029         {
5030             XmlTextWriter xw = new XmlTextWriter( fileName, null );
5031             try {
5032                 xw.Formatting = Formatting.Indented;
5033                 xw.WriteStartDocument(true);
5034                 WriteXmlSchema(xw, writeHierarchy);
5035                 xw.WriteEndDocument();
5036             }
5037             finally {
5038                 xw.Close();
5039             }
5040         }
5041
5042         public XmlReadMode ReadXml(Stream stream)
5043         {
5044             if (stream == null)
5045                 return XmlReadMode.Auto;
5046
5047             return ReadXml( new XmlTextReader(stream), false);
5048         }
5049
5050         public XmlReadMode ReadXml(TextReader reader)
5051         {
5052             if (reader == null)
5053                 return XmlReadMode.Auto;
5054
5055             return ReadXml( new XmlTextReader(reader), false);
5056         }
5057
5058         [ResourceExposure(ResourceScope.Machine)]
5059         [ResourceConsumption(ResourceScope.Machine)]
5060         public XmlReadMode ReadXml(string fileName)
5061         {
5062             XmlTextReader xr = new XmlTextReader(fileName);
5063             try {
5064                 return ReadXml( xr , false);
5065             }
5066             finally {
5067                 xr.Close();
5068             }
5069         }
5070
5071         public XmlReadMode ReadXml(XmlReader reader)
5072         {
5073             return ReadXml(reader, false);
5074         }
5075
5076         private void RestoreConstraint(bool originalEnforceConstraint) {
5077             if (this.DataSet != null) {
5078                 this.DataSet.EnforceConstraints = originalEnforceConstraint;
5079             }
5080             else {
5081                 this.EnforceConstraints = originalEnforceConstraint;
5082             }
5083         }
5084
5085         private bool IsEmptyXml(XmlReader reader) {
5086             if (reader.IsEmptyElement) {
5087                 if (reader.AttributeCount == 0 || (reader.LocalName == Keywords.DIFFGRAM && reader.NamespaceURI == Keywords.DFFNS)) {
5088                     return true;
5089                 }
5090                 if (reader.AttributeCount == 1) {
5091                     reader.MoveToAttribute(0);
5092                     if ((this.Namespace == reader.Value) &&
5093                         (this.Prefix == reader.LocalName) &&
5094                         (reader.Prefix == Keywords.XMLNS) &&
5095                         (reader.NamespaceURI == Keywords.XSD_XMLNS_NS))
5096                     return true;
5097                 }
5098             }
5099             return false;
5100         }
5101
5102         internal XmlReadMode ReadXml(XmlReader reader, bool denyResolving)
5103         {
5104             IntPtr hscp;
5105             Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXml|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
5106             try {
5107
5108               DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
5109               try {
5110                 bool fDataFound = false;
5111                 bool fSchemaFound = false;
5112                 bool fDiffsFound = false;
5113                 bool fIsXdr = false;
5114                 int iCurrentDepth = -1;
5115                 XmlReadMode ret = XmlReadMode.Auto;
5116
5117                 // clear the hashtable to avoid conflicts between diffgrams, SqlHotFix 782
5118                 rowDiffIdUsage.Prepare(this);
5119
5120                 if (reader == null)
5121                     return ret;
5122                 bool originalEnforceConstraint = false;
5123                 if (this.DataSet != null) {
5124                     originalEnforceConstraint = this.DataSet.EnforceConstraints;
5125                     this.DataSet.EnforceConstraints = false;
5126                 }
5127                 else {
5128                     originalEnforceConstraint = this.EnforceConstraints;
5129                     this.EnforceConstraints = false;
5130                 }
5131
5132                 if (reader is XmlTextReader)
5133                     ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
5134
5135                 XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5136                 XmlDataLoader xmlload = null;
5137
5138
5139                 reader.MoveToContent();
5140                 if (Columns.Count == 0) {
5141                     if (IsEmptyXml(reader)) {
5142                         reader.Read();
5143                         return ret;
5144                     }
5145                 }
5146
5147                 if (reader.NodeType == XmlNodeType.Element) {
5148                     iCurrentDepth = reader.Depth;
5149
5150                     if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5151                         if (Columns.Count == 0) {
5152                             if (reader.IsEmptyElement) {
5153                                 reader.Read();
5154                                 return XmlReadMode.DiffGram;
5155                             }
5156                             throw ExceptionBuilder.DataTableInferenceNotSupported();
5157                         }
5158                         this.ReadXmlDiffgram(reader);
5159                         // read the closing tag of the current element
5160                         ReadEndElement(reader);
5161
5162                         RestoreConstraint(originalEnforceConstraint);
5163                         return XmlReadMode.DiffGram;
5164                     }
5165
5166                     // if reader points to the schema load it
5167                     if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5168                         // load XDR schema and exit
5169                         ReadXDRSchema(reader);
5170
5171                         RestoreConstraint(originalEnforceConstraint);
5172                         return XmlReadMode.ReadSchema; //since the top level element is a schema return
5173                     }
5174
5175                     if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5176                         // load XSD schema and exit
5177                         ReadXmlSchema(reader, denyResolving);
5178                         RestoreConstraint(originalEnforceConstraint);
5179                         return XmlReadMode.ReadSchema; //since the top level element is a schema return
5180                     }
5181
5182                     if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal)) {
5183                         if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5184                             this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5185                         }
5186                         else {
5187                             this.enforceConstraints = originalEnforceConstraint;
5188                         }
5189
5190                         throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5191                     }
5192
5193                     // now either the top level node is a table and we load it through dataReader...
5194
5195                     // ... or backup the top node and all its attributes because we may need to InferSchema
5196                     XmlElement topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5197                     if (reader.HasAttributes) {
5198                         int attrCount = reader.AttributeCount;
5199                         for (int i=0;i<attrCount;i++) {
5200                             reader.MoveToAttribute(i);
5201                             if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
5202                                 topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
5203                             else {
5204                                 XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
5205                                 attr.Prefix = reader.Prefix;
5206                                 attr.Value = reader.GetAttribute(i);
5207                             }
5208                         }
5209                     }
5210                     reader.Read();
5211
5212                     while(MoveToElement(reader, iCurrentDepth)) {
5213
5214                         if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5215                             this.ReadXmlDiffgram(reader);
5216                             // read the closing tag of the current element
5217                             ReadEndElement(reader);
5218                             RestoreConstraint(originalEnforceConstraint);
5219                             return XmlReadMode.DiffGram;
5220                         }
5221
5222                         // if reader points to the schema load it...
5223
5224
5225                         if (!fSchemaFound && !fDataFound && reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5226                             // load XDR schema and exit
5227                             ReadXDRSchema(reader);
5228                             fSchemaFound = true;
5229                             fIsXdr = true;
5230                             continue;
5231                         }
5232
5233                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5234                             // load XSD schema and exit
5235                             ReadXmlSchema(reader, denyResolving);
5236                             fSchemaFound = true;
5237                             continue;
5238                         }
5239
5240                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
5241                             if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5242                                 this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5243                             }
5244                             else {
5245                                 this.enforceConstraints = originalEnforceConstraint;
5246                             }
5247                             throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5248                         }
5249
5250                         if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5251                             this.ReadXmlDiffgram(reader);
5252                             fDiffsFound = true;
5253                             ret = XmlReadMode.DiffGram;
5254                         }
5255                         else {
5256                             // we found data here
5257                             fDataFound = true;
5258
5259                             if (!fSchemaFound && Columns.Count == 0) {
5260                                 XmlNode node = xdoc.ReadNode(reader);
5261                                 topNode.AppendChild(node);
5262                             }
5263                             else {
5264                                 if (xmlload == null)
5265                                     xmlload = new XmlDataLoader(this, fIsXdr, topNode, false);
5266                                 xmlload.LoadData(reader);
5267                                 if (fSchemaFound)
5268                                     ret = XmlReadMode.ReadSchema;
5269                                 else
5270                                     ret = XmlReadMode.IgnoreSchema;
5271                             }
5272                         }
5273
5274                     }
5275                     // read the closing tag of the current element
5276                     ReadEndElement(reader);
5277
5278                     // now top node contains the data part
5279                     xdoc.AppendChild(topNode);
5280
5281                     if (!fSchemaFound && Columns.Count == 0) {
5282                         if (IsEmptyXml(reader)) {
5283                             reader.Read();
5284                             return ret;
5285                         }
5286                         throw ExceptionBuilder.DataTableInferenceNotSupported();
5287                     }
5288
5289                     if (xmlload == null)
5290                         xmlload = new XmlDataLoader(this, fIsXdr, false);
5291
5292                     // so we InferSchema
5293                     if (!fDiffsFound) {// we need to add support for inference here
5294                     }
5295                 }
5296                 RestoreConstraint(originalEnforceConstraint);
5297                 return ret;
5298               }
5299               finally {
5300                 rowDiffIdUsage.Cleanup();
5301               }
5302             }
5303             finally{
5304                 Bid.ScopeLeave(ref hscp);
5305             }
5306         }
5307
5308         internal XmlReadMode ReadXml(XmlReader reader, XmlReadMode mode, bool denyResolving)
5309         {
5310             DataTable.RowDiffIdUsageSection rowDiffIdUsage = new DataTable.RowDiffIdUsageSection();
5311             try {
5312                 bool fSchemaFound = false;
5313                 bool fDataFound = false;
5314                 bool fIsXdr = false;
5315                 int iCurrentDepth = -1;
5316                 XmlReadMode ret = mode;
5317
5318                 // Dev11 904428: prepare and cleanup rowDiffId hashtable
5319                 rowDiffIdUsage.Prepare(this);
5320
5321                 if (reader == null)
5322                     return ret;
5323
5324                 bool originalEnforceConstraint  = false;
5325                 if (this.DataSet != null) {
5326                     originalEnforceConstraint  = this.DataSet.EnforceConstraints;
5327                     this.DataSet.EnforceConstraints = false;
5328                 }
5329                 else {
5330                     originalEnforceConstraint  = this.EnforceConstraints;
5331                     this.EnforceConstraints = false;
5332                 }
5333
5334                 if (reader is XmlTextReader)
5335                     ((XmlTextReader) reader).WhitespaceHandling = WhitespaceHandling.Significant;
5336
5337                 XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5338
5339                 if ((mode != XmlReadMode.Fragment) && (reader.NodeType == XmlNodeType.Element))
5340                     iCurrentDepth = reader.Depth;
5341
5342                 reader.MoveToContent();
5343                 if (Columns.Count == 0) {
5344                     if (IsEmptyXml(reader)) {
5345                         reader.Read();
5346                         return ret;
5347                     }
5348                 }
5349
5350                 XmlDataLoader xmlload = null;
5351
5352                 if (reader.NodeType == XmlNodeType.Element) {
5353                     XmlElement topNode = null;
5354                     if (mode == XmlReadMode.Fragment) {
5355                         xdoc.AppendChild(xdoc.CreateElement("ds_sqlXmlWraPPeR"));
5356                         topNode = xdoc.DocumentElement;
5357                     }
5358                     else { //handle the top node
5359                         if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5360                             if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
5361                                 if (Columns.Count == 0) {
5362                                     if(reader.IsEmptyElement) {
5363                                         reader.Read();
5364                                         return XmlReadMode.DiffGram;
5365                                     }
5366                                     throw ExceptionBuilder.DataTableInferenceNotSupported();
5367                                 }
5368                                 this.ReadXmlDiffgram(reader);
5369                                 // read the closing tag of the current element
5370                                 ReadEndElement(reader);
5371                             }
5372                             else {
5373                                 reader.Skip();
5374                             }
5375                             RestoreConstraint(originalEnforceConstraint);
5376                             return ret;
5377                         }
5378
5379                         if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5380                             // load XDR schema and exit
5381                             if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema)) {
5382                                 ReadXDRSchema(reader);
5383                             }
5384                             else {
5385                                 reader.Skip();
5386                             }
5387                             RestoreConstraint(originalEnforceConstraint);
5388                             return ret; //since the top level element is a schema return
5389                         }
5390
5391                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5392                             // load XSD schema and exit
5393                             if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
5394                                 ReadXmlSchema(reader, denyResolving);
5395                             }
5396                             else
5397                                 reader.Skip();
5398                             RestoreConstraint(originalEnforceConstraint);
5399                             return ret; //since the top level element is a schema return
5400                         }
5401
5402                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
5403                              if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5404                                  this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5405                              }
5406                              else {
5407                                 this.enforceConstraints = originalEnforceConstraint;
5408                             }
5409                             throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5410                         }
5411
5412                         // now either the top level node is a table and we load it through dataReader...
5413                         // ... or backup the top node and all its attributes
5414                         topNode = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5415                         if (reader.HasAttributes) {
5416                             int attrCount = reader.AttributeCount;
5417                             for (int i=0;i<attrCount;i++) {
5418                                 reader.MoveToAttribute(i);
5419                                 if (reader.NamespaceURI.Equals(Keywords.XSD_XMLNS_NS))
5420                                     topNode.SetAttribute(reader.Name, reader.GetAttribute(i));
5421                                 else {
5422                                     XmlAttribute attr = topNode.SetAttributeNode(reader.LocalName, reader.NamespaceURI);
5423                                     attr.Prefix = reader.Prefix;
5424                                     attr.Value = reader.GetAttribute(i);
5425                                 }
5426                             }
5427                         }
5428                         reader.Read();
5429                     }
5430
5431                     while(MoveToElement(reader, iCurrentDepth)) {
5432
5433                         if (reader.LocalName == Keywords.XDR_SCHEMA && reader.NamespaceURI==Keywords.XDRNS) {
5434                             // load XDR schema
5435                             if (!fSchemaFound && !fDataFound && (mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
5436                                 ReadXDRSchema(reader);
5437                                 fSchemaFound = true;
5438                                 fIsXdr = true;
5439                             }
5440                             else {
5441                                 reader.Skip();
5442                             }
5443                             continue;
5444                         }
5445
5446                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5447                         // load XSD schema and exit
5448                             if ((mode != XmlReadMode.IgnoreSchema) && (mode != XmlReadMode.InferSchema))  {
5449                                 ReadXmlSchema(reader, denyResolving);
5450                                 fSchemaFound = true;
5451                             }
5452                             else {
5453                                 reader.Skip();
5454                             }
5455                             continue;
5456                         }
5457
5458                         if ((reader.LocalName == Keywords.DIFFGRAM) && (reader.NamespaceURI == Keywords.DFFNS)) {
5459                             if ((mode == XmlReadMode.DiffGram) || (mode == XmlReadMode.IgnoreSchema)) {
5460                                 if (Columns.Count == 0) {
5461                                     if(reader.IsEmptyElement) {
5462                                         reader.Read();
5463                                         return XmlReadMode.DiffGram;
5464                                     }
5465                                     throw ExceptionBuilder.DataTableInferenceNotSupported();
5466                                 }
5467                                 this.ReadXmlDiffgram(reader);
5468                                 ret = XmlReadMode.DiffGram;
5469                             }
5470                             else {
5471                                 reader.Skip();
5472                             }
5473                             continue;
5474                         }
5475
5476                         if (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI.StartsWith(Keywords.XSD_NS_START, StringComparison.Ordinal))  {
5477                             if (this.DataSet != null) { // we should not throw for constraint, we already will throw for unsupported schema, so restore enforce cost, but not via property
5478                                  this.DataSet.RestoreEnforceConstraints(originalEnforceConstraint);
5479                             }
5480                             else {
5481                                 this.enforceConstraints = originalEnforceConstraint;
5482                             }
5483                             throw ExceptionBuilder.DataSetUnsupportedSchema(Keywords.XSDNS);
5484                         }
5485
5486                         if (mode == XmlReadMode.DiffGram) {
5487                             reader.Skip();
5488                             continue; // we do not read data in diffgram mode
5489                         }
5490
5491                         // if we are here we found some data
5492                         fDataFound = true;
5493
5494                         if (mode == XmlReadMode.InferSchema) { //save the node in DOM until the end;
5495                             XmlNode node = xdoc.ReadNode(reader);
5496                             topNode.AppendChild(node);
5497                         }
5498                         else {
5499                             if (Columns.Count == 0) {
5500                                 throw ExceptionBuilder.DataTableInferenceNotSupported();
5501                             }
5502                             if (xmlload == null)
5503                                 xmlload = new XmlDataLoader(this, fIsXdr, topNode,  mode == XmlReadMode.IgnoreSchema);
5504                             xmlload.LoadData(reader);
5505                         }
5506                     } //end of the while
5507
5508                     // read the closing tag of the current element
5509                     ReadEndElement(reader);
5510
5511                     // now top node contains the data part
5512                     xdoc.AppendChild(topNode);
5513
5514                     if (xmlload == null)
5515                         xmlload = new XmlDataLoader(this, fIsXdr, mode == XmlReadMode.IgnoreSchema);
5516
5517                     if (mode == XmlReadMode.DiffGram) {
5518                         // we already got the diffs through XmlReader interface
5519                         RestoreConstraint(originalEnforceConstraint);
5520                         return ret;
5521                     }
5522     //todo
5523                     // Load Data
5524                     if (mode == XmlReadMode.InferSchema) {
5525                         if (Columns.Count == 0) {
5526                             throw ExceptionBuilder.DataTableInferenceNotSupported();
5527                         }
5528
5529     // [....]                    xmlload.InferSchema(xdoc, null);
5530     // [....]                    xmlload.LoadData(xdoc);
5531                     }
5532                 }
5533                 RestoreConstraint(originalEnforceConstraint);
5534
5535                 return ret;
5536             }
5537             finally {
5538                 // Dev11 904428: prepare and cleanup rowDiffId hashtable
5539                 rowDiffIdUsage.Cleanup();
5540             }
5541         }
5542
5543
5544         internal void ReadEndElement(XmlReader reader) {
5545             while (reader.NodeType == XmlNodeType.Whitespace) {
5546                 reader.Skip();
5547             }
5548             if (reader.NodeType == XmlNodeType.None) {
5549                 reader.Skip();
5550             }
5551             else if (reader.NodeType == XmlNodeType.EndElement) {
5552                 reader.ReadEndElement();
5553             }
5554         }
5555         internal void ReadXDRSchema(XmlReader reader) {
5556             XmlDocument xdoc = new XmlDocument(); // we may need this to infer the schema
5557             XmlNode schNode = xdoc.ReadNode(reader);;
5558             //consume and ignore it - No support
5559         }
5560
5561         internal bool MoveToElement(XmlReader reader, int depth) {
5562             while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement && reader.NodeType != XmlNodeType.Element && reader.Depth > depth) {
5563                 reader.Read();
5564             }
5565             return (reader.NodeType == XmlNodeType.Element);
5566         }
5567         private void ReadXmlDiffgram(XmlReader reader) { // fill correctly
5568             int d = reader.Depth;
5569             bool fEnforce = this.EnforceConstraints;
5570             this.EnforceConstraints =false;
5571             DataTable newDt;
5572             bool isEmpty;
5573
5574             if (this.Rows.Count == 0) {
5575                 isEmpty = true;
5576                 newDt = this;
5577             }
5578             else {
5579                 isEmpty = false;
5580                 newDt = this.Clone();
5581                 newDt.EnforceConstraints = false;
5582             }
5583
5584             newDt.Rows.nullInList = 0;
5585
5586             reader.MoveToContent();
5587
5588             if ((reader.LocalName != Keywords.DIFFGRAM) && (reader.NamespaceURI != Keywords.DFFNS))
5589                 return;
5590             reader.Read();
5591             if (reader.NodeType == XmlNodeType.Whitespace) {
5592                 MoveToElement(reader, reader.Depth - 1 /*iCurrentDepth*/); // skip over whitespaces.
5593             }
5594
5595             newDt.fInLoadDiffgram = true;
5596
5597             if (reader.Depth > d) {
5598                 if ((reader.NamespaceURI != Keywords.DFFNS) && (reader.NamespaceURI != Keywords.MSDNS)) {
5599                     //we should be inside the dataset part
5600                     XmlDocument xdoc = new XmlDocument();
5601                     XmlElement node = xdoc.CreateElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
5602                     reader.Read();
5603                     if (reader.Depth-1 > d) {
5604                         XmlDataLoader xmlload = new XmlDataLoader(newDt, false, node, false);
5605                         xmlload.isDiffgram = true; // turn on the special processing
5606                         xmlload.LoadData(reader);
5607                     }
5608                     ReadEndElement(reader);
5609                 }
5610                 if (((reader.LocalName == Keywords.SQL_BEFORE) && (reader.NamespaceURI == Keywords.DFFNS)) ||
5611                     ((reader.LocalName == Keywords.MSD_ERRORS) && (reader.NamespaceURI == Keywords.DFFNS)))
5612
5613                 {
5614                     //this will consume the changes and the errors part
5615                     XMLDiffLoader diffLoader = new XMLDiffLoader();
5616                     diffLoader.LoadDiffGram(newDt, reader);
5617                 }
5618
5619                 // get to the closing diff tag
5620                 while(reader.Depth > d) {
5621                     reader.Read();
5622                 }
5623                 // read the closing tag
5624                 ReadEndElement(reader);
5625             }
5626
5627             if (newDt.Rows.nullInList > 0)
5628                 throw ExceptionBuilder.RowInsertMissing(newDt.TableName);
5629
5630
5631             newDt.fInLoadDiffgram = false;
5632             List<DataTable> tableList = new List<DataTable>();
5633             tableList.Add(this);
5634             CreateTableList(this, tableList);
5635
5636 // this is terrible, optimize it
5637             for (int i = 0; i < tableList.Count ; i++) {
5638                 DataRelation[] relations = tableList[i].NestedParentRelations;
5639                 foreach(DataRelation rel in relations) {
5640                     if (rel != null && rel.ParentTable == tableList[i]) {
5641                         foreach (DataRow r in tableList[i].Rows) {
5642                             foreach (DataRelation rel2 in relations) {
5643                                 r.CheckForLoops(rel2);
5644                             }
5645                         }
5646                     }
5647                 }
5648             }
5649
5650             if (!isEmpty) {
5651                 this.Merge(newDt);
5652             }
5653             this.EnforceConstraints = fEnforce;
5654         }
5655
5656         internal void ReadXSDSchema(XmlReader reader, bool denyResolving) {
5657             XmlSchemaSet sSet = new XmlSchemaSet();
5658             while (reader.LocalName == Keywords.XSD_SCHEMA && reader.NamespaceURI==Keywords.XSDNS) {
5659                 XmlSchema s = XmlSchema.Read(reader, null);
5660                 sSet.Add(s);
5661                 //read the end tag
5662                 ReadEndElement(reader);
5663             }
5664             sSet.Compile();
5665
5666             XSDSchema schema = new XSDSchema();
5667             schema.LoadSchema(sSet, this);
5668         }
5669
5670
5671         public void ReadXmlSchema(Stream stream)
5672         {
5673             if (stream == null)
5674                 return;
5675
5676             ReadXmlSchema( new XmlTextReader( stream ), false );
5677         }
5678
5679         public void ReadXmlSchema(TextReader reader)
5680         {
5681             if (reader == null)
5682                 return;
5683
5684             ReadXmlSchema( new XmlTextReader( reader ), false );
5685         }
5686
5687         [ResourceExposure(ResourceScope.Machine)]
5688         [ResourceConsumption(ResourceScope.Machine)]
5689         public void ReadXmlSchema(String fileName)
5690         {
5691             XmlTextReader xr = new XmlTextReader(fileName);
5692             try {
5693                 ReadXmlSchema( xr, false );
5694             }
5695             finally {
5696                 xr.Close();
5697             }
5698         }
5699
5700         public void ReadXmlSchema(XmlReader reader)
5701         {
5702             ReadXmlSchema(reader, false);
5703         }
5704
5705         internal void ReadXmlSchema(XmlReader reader, bool denyResolving)
5706         {
5707             IntPtr hscp;
5708             Bid.ScopeEnter(out hscp, "<ds.DataTable.ReadXmlSchema|INFO> %d#, denyResolving=%d{bool}\n", ObjectID, denyResolving);
5709             try{
5710                 DataSet ds = new DataSet();
5711                 SerializationFormat cachedRemotingFormat = this.RemotingFormat;
5712                 // fxcop: ReadXmlSchema will provide the CaseSensitive, Locale, Namespace information
5713                 ds.ReadXmlSchema(reader, denyResolving);
5714
5715                 string CurrentTableFullName = ds.MainTableName;
5716
5717                 if (Common.ADP.IsEmpty(this.tableName) && Common.ADP.IsEmpty(CurrentTableFullName))
5718                     return;
5719
5720                 DataTable currentTable = null;
5721
5722                 if (!Common.ADP.IsEmpty(this.tableName)) {
5723                     if (!Common.ADP.IsEmpty(this.Namespace)) {
5724                         currentTable = ds.Tables[this.tableName, this.Namespace];
5725                     }
5726                     else {//SQL BU defect tracking 240293
5727                         int tableIndex = ds.Tables.InternalIndexOf(this.tableName);
5728                         if (tableIndex  > -1) {
5729                             currentTable = ds.Tables[tableIndex];
5730                         }
5731                     }
5732                 }
5733                 else{  //!Common.ADP.IsEmpty(CurrentTableFullName)
5734                     string CurrentTableNamespace = "";
5735                     int nsSeperator = CurrentTableFullName.IndexOf(':');
5736                     if (nsSeperator > -1) {
5737                         CurrentTableNamespace = CurrentTableFullName.Substring(0, nsSeperator);
5738                     }
5739                     string CurrentTableName = CurrentTableFullName.Substring(nsSeperator + 1, CurrentTableFullName.Length - nsSeperator -1);
5740
5741                     currentTable = ds.Tables[CurrentTableName, CurrentTableNamespace];
5742                 }
5743
5744                 if (currentTable == null) { // bug fix :99186
5745                     string qTableName = string.Empty;
5746                     if (!Common.ADP.IsEmpty(this.tableName)) {
5747                         qTableName = (this.Namespace.Length > 0)? (this.Namespace + ":" + this.tableName):this.tableName;
5748                     }
5749                     else {
5750                         qTableName = CurrentTableFullName ;
5751                     }
5752                     throw ExceptionBuilder.TableNotFound(qTableName);
5753                 }
5754
5755                 currentTable._remotingFormat = cachedRemotingFormat;
5756
5757                 List<DataTable> tableList = new List<DataTable>();
5758                 tableList.Add(currentTable);
5759                 CreateTableList(currentTable, tableList);
5760                 List<DataRelation> relationList = new List<DataRelation>();
5761                 CreateRelationList(tableList, relationList);
5762
5763                 if (relationList.Count == 0) {
5764                     if (this.Columns.Count == 0) {
5765                         DataTable tempTable = currentTable;
5766                         if (tempTable != null)
5767                             tempTable.CloneTo(this, null, false); // we may have issue Amir
5768                         if (this.DataSet == null && this.tableNamespace == null) { // webdata 105506
5769 // for standalone table, clone wont get these correctly, since they may come with inheritance
5770                             this.tableNamespace =  tempTable.Namespace;
5771                         }
5772                     }
5773                     return;
5774                 }
5775                 else {
5776                     if (Common.ADP.IsEmpty(this.TableName)) {
5777                         this.TableName = currentTable.TableName;
5778                         if (!Common.ADP.IsEmpty(currentTable.Namespace)) {
5779                             this.Namespace = currentTable.Namespace;
5780                         }
5781                     }
5782                     if (this.DataSet == null) {
5783                         DataSet dataset = new DataSet(ds.DataSetName);
5784 // webdata 105506
5785                         // if user set values on DataTable, it isn't necessary
5786                         // to set them on the DataSet because they won't be inherited
5787                         // but it is simpler to set them in both places
5788
5789                         // if user did not set values on DataTable, it is required
5790                         // to set them on the DataSet so the table will inherit
5791                         // the value already on the Datatable
5792                         dataset.SetLocaleValue(ds.Locale, ds.ShouldSerializeLocale());
5793                         dataset.CaseSensitive = ds.CaseSensitive;
5794                         dataset.Namespace = ds.Namespace;
5795                         dataset.mainTableName = ds.mainTableName;
5796                         dataset.RemotingFormat = ds.RemotingFormat;
5797
5798                         dataset.Tables.Add(this);
5799                     }
5800
5801                     DataTable targetTable = CloneHierarchy(currentTable, this.DataSet, null);
5802
5803                     foreach(DataTable tempTable in tableList) {
5804                         DataTable destinationTable = this.DataSet.Tables[tempTable.tableName, tempTable.Namespace];
5805                         DataTable sourceTable = ds.Tables[tempTable.tableName, tempTable.Namespace];
5806                         foreach(Constraint tempConstrain in sourceTable.Constraints) {
5807                             ForeignKeyConstraint fkc = tempConstrain as ForeignKeyConstraint;  // we have already cloned the UKC when cloning the datatable
5808                             if (fkc != null) {
5809                                 if (fkc.Table != fkc.RelatedTable)  {
5810                                     if (tableList.Contains(fkc.Table) && tableList.Contains(fkc.RelatedTable)) {
5811                                         ForeignKeyConstraint newFKC = (ForeignKeyConstraint)fkc.Clone(destinationTable.DataSet);
5812                                         if (!destinationTable.Constraints.Contains(newFKC.ConstraintName))
5813                                             destinationTable.Constraints.Add(newFKC); // we know that the dest table is already in the table
5814                                     }
5815                                 }
5816                            }
5817                         }
5818                     }
5819                     foreach(DataRelation rel in relationList) {
5820                         if (!this.DataSet.Relations.Contains(rel.RelationName))
5821                             this.DataSet.Relations.Add(rel.Clone(this.DataSet));
5822                     }
5823
5824                     bool hasExternaldependency = false;
5825
5826                     foreach(DataTable tempTable in tableList) {
5827                         foreach(DataColumn dc in tempTable.Columns) {
5828                             hasExternaldependency = false;
5829                             if (dc.Expression.Length  != 0) {
5830                                 DataColumn[] dependency = dc.DataExpression.GetDependency();
5831                                 for (int j = 0; j < dependency.Length; j++) {
5832                                     if (!tableList.Contains(dependency[j].Table)) {
5833                                         hasExternaldependency = true;
5834                                         break;
5835                                     }
5836                                 }
5837                             }
5838                             if (!hasExternaldependency) {
5839                                 this.DataSet.Tables[tempTable.TableName, tempTable.Namespace].Columns[dc.ColumnName].Expression = dc.Expression;
5840                             }
5841                         }
5842                         hasExternaldependency = false;
5843                     }
5844
5845                 }
5846             }
5847             finally{
5848                 Bid.ScopeLeave(ref hscp);
5849             }
5850         }
5851
5852         private void CreateTableList(DataTable currentTable, List<DataTable> tableList) {
5853             foreach( DataRelation r in currentTable.ChildRelations ) {
5854                 if (! tableList.Contains(r.ChildTable)) {
5855                     tableList.Add(r.ChildTable);
5856                     CreateTableList(r.ChildTable, tableList);
5857                 }
5858             }
5859         }
5860         private void CreateRelationList(List<DataTable> tableList,  List<DataRelation> relationList) {
5861             foreach(DataTable table in tableList) {
5862                 foreach( DataRelation r in table.ChildRelations) {
5863                     if (tableList.Contains(r.ChildTable) && tableList.Contains(r.ParentTable)) {
5864                         relationList.Add(r);
5865                     }
5866                 }
5867             }
5868         }
5869
5870         /**************************************************************************
5871         The V2.0 (no V1.0 or V1.1) WSDL for Untyped DataTable being returned as a result (no parameters)
5872         <s:element name="anyUserSpecifiedMethodName">
5873             <!--  This is where parameters go -->
5874             <s:complexType /> 
5875         </s:element>
5876         <s:element name="anyUserSpecifiedMethodName"+"Response">
5877             <s:complexType>
5878                 <s:sequence>
5879                     <s:element minOccurs="0" maxOccurs="1" name="anyUserSpecifiedMethodName"+"Result">
5880                         <s:complexType>
5881                             <s:sequence>
5882                                 <s:any minOccurs="0" maxOccurs="unbounded" namespace="http://www.w3.org/2001/XMLSchema" processContents="lax" /> 
5883                                 <s:any minOccurs="1" namespace="urn:schemas-microsoft-com:xml-diffgram-v1" processContents="lax" /> 
5884                             </s:sequence>
5885                         </s:complexType>
5886                     </s:element>
5887                 </s:sequence>
5888             </s:complexType>
5889         </s:element>
5890
5891         Typed DataTable is not supported in WSDL (SQLBU 444636)
5892
5893         either fails because xsd generates its typed DataTable with an internal parameterless ctor
5894         
5895         or System.NullReferenceException: Object reference not set to an instance of an object.   (if namespace of StronglyTyped DataTable is not set)
5896            at System.Data.XmlTreeGen.FindTargetNamespace(DataTable table)
5897
5898         or System.InvalidOperationException: Schema Id is missing. The schema returned from WebServiceDataSetServer.Service+StudentsDataTable.GetSchema() must have an Id.
5899            at System.Xml.Serialization.SerializableMapping.RetrieveSerializableSchema()
5900         *****************************************************************************/
5901         public static XmlSchemaComplexType GetDataTableSchema(XmlSchemaSet schemaSet) {
5902             XmlSchemaComplexType type = new XmlSchemaComplexType();
5903             XmlSchemaSequence sequence = new XmlSchemaSequence();
5904             XmlSchemaAny any = new XmlSchemaAny();
5905             any.Namespace = XmlSchema.Namespace;
5906             any.MinOccurs = 0;
5907             any.MaxOccurs = Decimal.MaxValue;
5908             any.ProcessContents = XmlSchemaContentProcessing.Lax;
5909             sequence.Items.Add(any);
5910
5911             any = new XmlSchemaAny();
5912             any.Namespace = Keywords.DFFNS;
5913             any.MinOccurs = 1; // when recognizing WSDL - MinOccurs="0" denotes DataSet, a MinOccurs="1" for DataTable
5914             any.ProcessContents = XmlSchemaContentProcessing.Lax;
5915             sequence.Items.Add(any);
5916
5917             type.Particle = sequence;
5918
5919             return type;
5920         }
5921
5922         XmlSchema IXmlSerializable.GetSchema() {
5923             return GetSchema();
5924         }
5925
5926         protected virtual XmlSchema GetSchema() {
5927             if (GetType() == typeof(DataTable)) {
5928                 return null;
5929             }
5930             MemoryStream stream = new MemoryStream();
5931
5932             XmlWriter writer = new XmlTextWriter(stream, null);
5933             if (writer != null) {
5934                  (new XmlTreeGen(SchemaFormat.WebService)).Save(this, writer);
5935             }
5936             stream.Position = 0;
5937             return XmlSchema.Read(new XmlTextReader(stream), null);
5938         }
5939
5940         void IXmlSerializable.ReadXml(XmlReader reader) {
5941             IXmlTextParser textReader = reader as IXmlTextParser;
5942             bool fNormalization = true;
5943             if (textReader != null) {
5944                 fNormalization = textReader.Normalized;
5945                 textReader.Normalized = false;
5946             }
5947             ReadXmlSerializable(reader);
5948
5949             if (textReader != null) {
5950                 textReader.Normalized = fNormalization;
5951             }
5952         }
5953
5954         void IXmlSerializable.WriteXml(XmlWriter writer) {
5955             WriteXmlSchema(writer, false);
5956             WriteXml(writer, XmlWriteMode.DiffGram, false);
5957         }
5958
5959          protected virtual void ReadXmlSerializable(XmlReader reader) {
5960             ReadXml(reader, XmlReadMode.DiffGram, true);
5961         }
5962
5963 /*
5964         [
5965         DefaultValue(false),
5966         ResCategoryAttribute(Res.DataCategory_Data),
5967         ResDescriptionAttribute(Res.DataTableSerializeHierarchy)
5968         ]
5969         public bool SerializeHierarchy {
5970             get {
5971                 return this.serializeHierarchy;
5972             }
5973             set {
5974                 this.serializeHierarchy = value;
5975             }
5976         }
5977 */
5978
5979         // RowDiffIdUsageSection & DSRowDiffIdUsageSection Usage:
5980         //
5981         //        DataTable.[DS]RowDiffIdUsageSection rowDiffIdUsage = new DataTable.[DS]RowDiffIdUsageSection();
5982         //        try {
5983         //            rowDiffIdUsage.Prepare(DataTable or DataSet, depending on type);
5984         //
5985         //            // code that requires RowDiffId usage
5986         //
5987         //        }
5988         //        finally {
5989         //            rowDiffIdUsage.Cleanup();
5990         //        }
5991         // 
5992         // Nested calls are allowed on different tables. For example, user can register to row change events and trigger 
5993         // ReadXml on different table/ds). But, if user code will try to call ReadXml on table that is already in the scope,
5994         // this code will assert since nested calls on same table are unsupported.
5995         internal struct RowDiffIdUsageSection
5996         {
5997 #if DEBUG
5998             // This list contains tables currently used in diffgram processing, not including new tables that might be added later during.
5999             // if diffgram processing is not started, this value must be null. when it starts, relevant method should call Prepare.
6000             // Notes:
6001             // * in case of ReadXml on empty DataSet, this list can be initialized as empty (so empty list != null).
6002             // * only one scope is allowed on single thread, either for datatable or dataset
6003             // * assert is triggered if same table is added to this list twice
6004             // 
6005             // do not allocate TLS data in RETAIL bits!
6006             [ThreadStatic]
6007             internal static List<DataTable> s_usedTables;
6008 #endif //DEBUG
6009
6010             DataTable _targetTable;
6011
6012             internal void Prepare(DataTable table)
6013             {
6014                 Debug.Assert(_targetTable == null, "do not reuse this section");
6015                 Debug.Assert(table != null);
6016                 Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
6017 #if DEBUG
6018                 Debug.Assert(s_usedTables == null || !s_usedTables.Contains(table), 
6019                     "Nested call with same table can cause data corruption!");
6020 #endif //DEBUG
6021
6022 #if DEBUG
6023                 if (s_usedTables == null)
6024                     s_usedTables = new List<DataTable>();
6025                 s_usedTables.Add(table);
6026 #endif //DEBUG
6027                 _targetTable = table;
6028                 table.rowDiffId = null;
6029             }
6030
6031             [Conditional("DEBUG")]
6032             internal void Cleanup()
6033             {
6034                 // cannot assume target table was set - ThreadAbortException can be raised before Start is called
6035                 if (_targetTable != null)
6036                 {
6037 #if DEBUG
6038                     Debug.Assert(s_usedTables != null && s_usedTables.Contains(_targetTable), "missing Prepare before Cleanup");
6039                     if (s_usedTables != null)
6040                     {
6041                         s_usedTables.Remove(_targetTable);
6042                         if (s_usedTables.Count == 0)
6043                             s_usedTables = null;
6044                     }
6045 #endif //DEBUG
6046                     _targetTable.rowDiffId = null;
6047                 }
6048             }
6049
6050             [Conditional("DEBUG")]
6051             internal static void Assert(string message)
6052             {
6053 #if DEBUG
6054                 // this code asserts scope was created, but it does not assert that the table was included in it
6055                 // note that in case of DataSet, new tables might be added to the list in which case they won't appear in s_usedTables.
6056                 Debug.Assert(s_usedTables != null, message);
6057 #endif //DEBUG
6058             }
6059         }
6060
6061         internal struct DSRowDiffIdUsageSection
6062         {
6063             DataSet _targetDS;
6064
6065             internal void Prepare(DataSet ds)
6066             {
6067                 Debug.Assert(_targetDS == null, "do not reuse this section");
6068                 Debug.Assert(ds != null);
6069
6070                 _targetDS = ds;
6071 #if DEBUG
6072                 // initialize list of tables out of current tables
6073                 // note: it might remain empty (still initialization is needed for assert to operate)
6074                 if (RowDiffIdUsageSection.s_usedTables == null)
6075                     RowDiffIdUsageSection.s_usedTables = new List<DataTable>();
6076 #endif //DEBUG
6077                 for (int tableIndex = 0; tableIndex < ds.Tables.Count; ++tableIndex)
6078                 {
6079                     DataTable table = ds.Tables[tableIndex];
6080 #if DEBUG
6081                     Debug.Assert(!RowDiffIdUsageSection.s_usedTables.Contains(table), "Nested call with same table can cause data corruption!");
6082                     RowDiffIdUsageSection.s_usedTables.Add(table);
6083 #endif //DEBUG
6084                     Debug.Assert(table.rowDiffId == null, "rowDiffId wasn't previously cleared");
6085                     table.rowDiffId = null;
6086                 }
6087             }
6088
6089             [Conditional("DEBUG")]
6090             internal void Cleanup()
6091             {
6092                 // cannot assume target was set - ThreadAbortException can be raised before Start is called
6093                 if (_targetDS != null)
6094                 {
6095 #if DEBUG
6096                     Debug.Assert(RowDiffIdUsageSection.s_usedTables != null, "missing Prepare before Cleanup");
6097 #endif //DEBUG
6098
6099                     for (int tableIndex = 0; tableIndex < _targetDS.Tables.Count; ++tableIndex)
6100                     {
6101                         DataTable table = _targetDS.Tables[tableIndex];
6102 #if DEBUG
6103                         // cannot assert that table exists in the usedTables - new tables might be 
6104                         // created during diffgram processing in DataSet.ReadXml.
6105                         if (RowDiffIdUsageSection.s_usedTables != null)
6106                             RowDiffIdUsageSection.s_usedTables.Remove(table);
6107 #endif //DEBUG
6108                         table.rowDiffId = null;
6109                     }
6110 #if DEBUG
6111                     if (RowDiffIdUsageSection.s_usedTables != null && RowDiffIdUsageSection.s_usedTables.Count == 0)
6112                         RowDiffIdUsageSection.s_usedTables = null; // out-of-scope
6113 #endif //DEBUG
6114                 }
6115             }
6116         }
6117
6118         internal Hashtable RowDiffId {
6119             get {
6120                 // assert scope has been created either with RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare
6121                 RowDiffIdUsageSection.Assert("missing call to RowDiffIdUsageSection.Prepare or DSRowDiffIdUsageSection.Prepare");
6122
6123                 if (rowDiffId == null)
6124                     rowDiffId = new Hashtable();
6125                 return rowDiffId;
6126             }
6127         }
6128
6129         internal int ObjectID {
6130             get {
6131                 return _objectID;
6132             }
6133         }
6134
6135         internal void AddDependentColumn(DataColumn expressionColumn) {
6136             if (dependentColumns == null)
6137                 dependentColumns = new List<DataColumn>();
6138
6139             if (!dependentColumns.Contains(expressionColumn)) {
6140                 // only remember unique columns but expect non-unique columns to be added
6141                 dependentColumns.Add(expressionColumn);
6142             }
6143         }
6144
6145         internal void RemoveDependentColumn(DataColumn expressionColumn) {
6146             if (dependentColumns != null && dependentColumns.Contains(expressionColumn)) {
6147                 dependentColumns.Remove(expressionColumn);
6148             }
6149         }
6150
6151         internal void EvaluateExpressions() {
6152             //evaluates all expressions for all rows in table
6153             // SQLBU 414992: Serious performance issue when calling Merge
6154             // this improves performance by only computing expressions when they are present
6155             // and iterating over the rows instead of computing their position multiple times
6156             if ((null != dependentColumns) && (0 < dependentColumns.Count)) {
6157                 foreach(DataRow row in Rows) {
6158                     // only evaluate original values if different from current.
6159                     if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6160                         EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, null);
6161                     }
6162                     if (row.newRecord != -1) {
6163                         EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, null);
6164                     }
6165                     if (row.tempRecord != -1) {
6166                         EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, null);
6167                     }
6168                 }
6169             }
6170         }
6171
6172         internal void EvaluateExpressions(DataRow row, DataRowAction action, List<DataRow> cachedRows) {
6173             // evaluate all expressions for specified row
6174             if (action == DataRowAction.Add ||
6175                 action == DataRowAction.Change||
6176                 (action == DataRowAction.Rollback && (row.oldRecord!=-1 || row.newRecord!=-1))) {
6177                  // only evaluate original values if different from current.
6178                 if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6179                     EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Original, cachedRows);
6180                 }
6181                 if (row.newRecord != -1) {
6182                     EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Current, cachedRows);
6183                 }
6184                 if (row.tempRecord != -1) {
6185                     EvaluateDependentExpressions(dependentColumns, row, DataRowVersion.Proposed, cachedRows);
6186                 }
6187                 return;
6188             }
6189             else if ((action == DataRowAction.Delete || (action==DataRowAction.Rollback && row.oldRecord==-1 && row.newRecord==-1)) && dependentColumns != null) {
6190                 foreach(DataColumn col in dependentColumns) {                    
6191                     if (col.DataExpression != null && col.DataExpression.HasLocalAggregate() && col.Table == this) {
6192                         for (int j = 0; j < Rows.Count; j++) {
6193                             DataRow tableRow = Rows[j];
6194
6195                             if (tableRow.oldRecord != -1 && tableRow.oldRecord != tableRow.newRecord) {
6196                                 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Original, null);
6197                             }                          
6198                         }
6199                         for (int j = 0; j < Rows.Count; j++)
6200                         {
6201                             DataRow tableRow = Rows[j];
6202                          
6203                             if (tableRow.tempRecord != -1)
6204                             {
6205                                 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Proposed, null);
6206                             }                           
6207                         }
6208                         // VSTFDEVDIV911434: Order is important here - we need to update proposed before current
6209                         // Oherwise rows that are in edit state will get ListChanged/PropertyChanged event before default value is changed
6210                         // It is also the reason why we are not doping it in the single loop: EvaluateDependentExpression can update the
6211                         // whole table, if it happens, current for all but first row is updated before proposed value
6212                         for (int j = 0; j < Rows.Count; j++)
6213                         {
6214                             DataRow tableRow = Rows[j];
6215                                                      
6216                             if (tableRow.newRecord != -1)
6217                             {
6218                                 EvaluateDependentExpressions(dependentColumns, tableRow, DataRowVersion.Current, null);
6219                             }
6220                         }
6221                         break;
6222                     }
6223                 }
6224
6225                 if (cachedRows != null) {
6226                     foreach (DataRow relatedRow in cachedRows) {
6227                         if (relatedRow.oldRecord != -1 && relatedRow.oldRecord != relatedRow.newRecord) {
6228                             relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Original, null);
6229                         }
6230                         if (relatedRow.newRecord != -1) {
6231                             relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Current, null);
6232                         }
6233                         if (relatedRow.tempRecord != -1) {
6234                             relatedRow.Table.EvaluateDependentExpressions(relatedRow.Table.dependentColumns, relatedRow, DataRowVersion.Proposed, null);
6235                         }
6236                     }
6237                 }
6238             }
6239         }
6240
6241         internal void EvaluateExpressions(DataColumn column) {
6242             // evaluates all rows for expression from specified column
6243             Debug.Assert(column.Computed, "Only computed columns should be re-evaluated.");
6244             int count = column.table.Rows.Count;
6245             if (column.DataExpression.IsTableAggregate() && count > 0) {
6246                 // this value is a constant across the table.
6247                 object aggCurrent = column.DataExpression.Evaluate();
6248                 for (int j = 0; j < count; j++) {
6249                     DataRow row = column.table.Rows[j];
6250                     // only evaluate original values if different from current.
6251                     if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6252                         column[row.oldRecord] = aggCurrent;
6253                     }
6254                     if (row.newRecord != -1) {
6255                         column[row.newRecord] = aggCurrent;
6256                     }
6257                     if (row.tempRecord != -1) {
6258                         column[row.tempRecord] = aggCurrent;
6259                     }
6260                 }
6261             }
6262             else {
6263                 for (int j = 0; j < count; j++) {
6264                     DataRow row = column.table.Rows[j];
6265
6266                     if (row.oldRecord != -1 && row.oldRecord != row.newRecord) {
6267                         column[row.oldRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Original);
6268                     }
6269                     if (row.newRecord != -1) {
6270                         column[row.newRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Current);
6271                     }
6272                     if (row.tempRecord != -1) {
6273                         column[row.tempRecord] = column.DataExpression.Evaluate(row, DataRowVersion.Proposed);
6274                     }
6275                 }
6276             }
6277
6278             // SQLBU 501916 - DataTable internal index is corrupted:'5'
6279             column.Table.ResetInternalIndexes(column);
6280             EvaluateDependentExpressions(column);
6281         }
6282
6283         internal void EvaluateDependentExpressions(DataColumn column) {
6284             // DataTable.Clear(), DataRowCollection.Clear() & DataColumn.set_Expression
6285             if (column.dependentColumns != null) {
6286                 foreach (DataColumn dc in column.dependentColumns) {
6287                     if ((dc.table != null) && !Object.ReferenceEquals(column, dc)) { // SQLBU 502736
6288                         EvaluateExpressions(dc);
6289                     }
6290                 }
6291             }
6292         }
6293
6294         internal void EvaluateDependentExpressions(List<DataColumn> columns, DataRow row, DataRowVersion version, List<DataRow> cachedRows) {
6295             if (columns == null)
6296                 return;
6297             //Expression evaluation is done first over same table expressions.
6298             int count = columns.Count;
6299             for(int i = 0; i < count; i++) {
6300                 if (columns[i].Table == this) {// if this column is in my table
6301                     DataColumn dc = columns[i];
6302                     if (dc.DataExpression != null && dc.DataExpression.HasLocalAggregate()) {
6303                     // if column expression references a local Table aggregate we need to recalc it for the each row in the local table
6304                         DataRowVersion expressionVersion  = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
6305                         bool isConst = dc.DataExpression.IsTableAggregate(); //is expression constant for entire table?
6306                         object newValue = null;
6307                         if (isConst) {  //if new value, just compute once
6308                             newValue = dc.DataExpression.Evaluate(row, expressionVersion);
6309                         }
6310                         for (int j = 0; j < Rows.Count; j++) { //evaluate for all rows in the table
6311                             DataRow dr = Rows[j];
6312                             if (dr.RowState == DataRowState.Deleted) {
6313                                 continue;
6314                             }
6315                             else if (expressionVersion == DataRowVersion.Original && (dr.oldRecord == -1 || dr.oldRecord == dr.newRecord)) {
6316                                 continue;
6317                             }
6318
6319                             if (!isConst) {
6320                                 newValue = dc.DataExpression.Evaluate(dr, expressionVersion);
6321                             }
6322                             SilentlySetValue(dr, dc, expressionVersion, newValue);
6323                         }
6324                     }
6325                     else {
6326                         if (row.RowState == DataRowState.Deleted) {
6327                             continue;
6328                         }
6329                         else if (version == DataRowVersion.Original && (row.oldRecord == -1 || row.oldRecord == row.newRecord)) {
6330                             continue;
6331                         }
6332                         SilentlySetValue(row, dc, version, dc.DataExpression == null ? dc.DefaultValue : dc.DataExpression.Evaluate(row, version));
6333                     }
6334                 }
6335             }
6336             // now do expression evaluation for expression columns other tables.
6337             count = columns.Count;
6338             for(int i = 0; i < count; i++) {
6339                 DataColumn dc = columns[i];
6340                 // if this column is NOT in my table or it is in the table and is not a local aggregate (self refs)
6341                 if (dc.Table != this || (dc.DataExpression != null && !dc.DataExpression.HasLocalAggregate())) {
6342                     DataRowVersion foreignVer = (version == DataRowVersion.Proposed) ? DataRowVersion.Default : version;
6343
6344                     // first - evaluate expressions for cachedRows (deletes & updates)
6345                     if (cachedRows != null) {
6346                         foreach (DataRow cachedRow in cachedRows) {
6347                             if (cachedRow.Table != dc.Table)
6348                                 continue;
6349                              // don't update original version if child row doesn't have an oldRecord.
6350                             if (foreignVer == DataRowVersion.Original && cachedRow.newRecord == cachedRow.oldRecord)
6351                                  continue;
6352                             if (cachedRow != null && ((cachedRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || cachedRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
6353                                 object newValue = dc.DataExpression.Evaluate(cachedRow, foreignVer);
6354                                 SilentlySetValue(cachedRow, dc, foreignVer, newValue);
6355                             }
6356                         }
6357                     }
6358
6359                     // next check parent relations
6360                     for (int j = 0; j < ParentRelations.Count; j++) {
6361                         DataRelation relation = ParentRelations[j];
6362                         if (relation.ParentTable != dc.Table)
6363                             continue;
6364                         foreach (DataRow parentRow in row.GetParentRows(relation, version)) {
6365                             if (cachedRows != null && cachedRows.Contains(parentRow))
6366                                 continue;
6367                              // don't update original version if child row doesn't have an oldRecord.
6368                             if (foreignVer == DataRowVersion.Original && parentRow.newRecord == parentRow.oldRecord)
6369                                  continue;
6370                             if (parentRow != null && ((parentRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || parentRow.oldRecord != -1))) {// if deleted GetRecordFromVersion will throw
6371                                 object newValue = dc.DataExpression.Evaluate(parentRow, foreignVer);
6372                                 SilentlySetValue(parentRow, dc, foreignVer, newValue);
6373                             }
6374                         }
6375                     }
6376                     // next check child relations
6377                     for (int j = 0; j < ChildRelations.Count; j++) {
6378                         DataRelation relation = ChildRelations[j];
6379                         if (relation.ChildTable != dc.Table)
6380                             continue;
6381                         foreach (DataRow childRow in row.GetChildRows(relation, version)) {
6382                             // don't update original version if child row doesn't have an oldRecord.
6383                             if (cachedRows != null && cachedRows.Contains(childRow))
6384                                 continue;
6385                             if (foreignVer == DataRowVersion.Original && childRow.newRecord == childRow.oldRecord)
6386                                 continue;
6387                             if (childRow != null && ((childRow.RowState != DataRowState.Deleted) && (version != DataRowVersion.Original || childRow.oldRecord != -1))) { // if deleted GetRecordFromVersion will throw
6388                                 object newValue = dc.DataExpression.Evaluate(childRow, foreignVer);
6389                                 SilentlySetValue(childRow, dc, foreignVer, newValue);
6390                             }
6391                         }
6392                     }
6393                 }
6394             }
6395         }
6396     }
6397 }
6398