Fixes for broken RO check
[mono.git] / mcs / class / System.Data / System.Data / MergeManager.cs
index e886d10b249d634ab1c3886e6beaeb35b367407c..911ca37f36fc848b535b135ee0a7c7f5fbcbf6ab 100644 (file)
@@ -37,12 +37,17 @@ namespace System.Data
                                throw new ArgumentNullException("targetSet");
                        if(sourceSet == null)
                                throw new ArgumentNullException("sourceSet");
+                       if (sourceSet == targetSet)
+                               return;
+
+                       bool prevEC = targetSet.EnforceConstraints;
+                       targetSet.EnforceConstraints = false;
 
                        foreach (DataTable t in sourceSet.Tables)
                                MergeManager.Merge(targetSet, t, preserveChanges, missingSchemaAction);
 
-                       AdjustSchema(targetSet,sourceSet,missingSchemaAction);
-
+                       AdjustSchemaRelations (targetSet, sourceSet, missingSchemaAction);
+                       targetSet.EnforceConstraints = prevEC;
                }
 
                internal static void Merge(DataSet targetSet, DataTable sourceTable, bool preserveChanges, MissingSchemaAction missingSchemaAction)
@@ -51,24 +56,40 @@ namespace System.Data
                                throw new ArgumentNullException("targetSet");
                        if(sourceTable == null)
                                throw new ArgumentNullException("sourceTable");
+                       if (sourceTable.DataSet == targetSet)
+                               return;
 
                        bool savedEnfoceConstraints = targetSet.EnforceConstraints;
                        targetSet.EnforceConstraints = false;
 
                        DataTable targetTable = null;
-                       if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
+                       if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
                                return;
-                       }
-                       if (targetTable != null) {
-                               checkColumnTypes(targetTable, sourceTable); // check that the colums datatype is the same
+                       if (targetTable != null)
                                fillData(targetTable, sourceTable, preserveChanges);
-                       }
                        targetSet.EnforceConstraints = savedEnfoceConstraints;
-                       
-                       if (!targetSet.EnforceConstraints && targetTable != null) {
-                               // indexes are still outdated
-                               targetTable.ResetIndexes();
-                       }
+               }
+
+               internal static void Merge (DataTable targetTable, 
+                                           DataTable sourceTable, 
+                                           bool preserveChanges, 
+                                           MissingSchemaAction missingSchemaAction)
+               {
+                       if(targetTable== null)
+                               throw new ArgumentNullException("targetTable");
+                       if(sourceTable == null)
+                               throw new ArgumentNullException("sourceTable");
+                       if (sourceTable == targetTable)
+                               return;
+
+                       bool savedEnforceConstraints = targetTable.EnforceConstraints;
+                       targetTable.EnforceConstraints = false;
+
+                       if (!AdjustSchema(targetTable, sourceTable, missingSchemaAction))
+                               return;
+
+                       fillData(targetTable, sourceTable, preserveChanges);
+                       targetTable.EnforceConstraints = savedEnforceConstraints;
                }
 
                internal static void Merge(DataSet targetSet, DataRow[] sourceRows, bool preserveChanges, MissingSchemaAction missingSchemaAction)
@@ -86,29 +107,21 @@ namespace System.Data
                                DataRow row = sourceRows[i];
                                DataTable sourceTable = row.Table;
                                DataTable targetTable = null;
-                               if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable)) {
+                               if (!AdjustSchema(targetSet, sourceTable, missingSchemaAction,ref targetTable))
                                        return;
-                               }
                                if (targetTable != null) {
-                                       checkColumnTypes(targetTable, row.Table);
                                        MergeRow(targetTable, row, preserveChanges);
-                                       if (!(targetTables.IndexOf(targetTable) >= 0)) {
+                                       if (!(targetTables.IndexOf(targetTable) >= 0))
                                                targetTables.Add(targetTable);
-                                       }
                                }
                        }
 
                        targetSet.EnforceConstraints = savedEnfoceConstraints;
-
-                       foreach(DataTable table in targetTables) {
-                               table.ResetIndexes();
-                       }
                }
 
                // merge a row into a target table.
                private static void MergeRow(DataTable targetTable, DataRow row, bool preserveChanges)
                {
-                       DataColumnCollection columns = row.Table.Columns;
                        DataColumn[] primaryKeys = targetTable.PrimaryKey;
                        DataRow targetRow = null;
                        DataRowVersion version = DataRowVersion.Default;
@@ -126,103 +139,123 @@ namespace System.Data
                                }
                        
                                // find the row in the target table.
-                               targetRow = targetTable.Rows.Find(keyValues);
+                               targetRow = targetTable.Rows.Find(keyValues, DataViewRowState.OriginalRows);
+                               if (targetRow == null)
+                                       targetRow = targetTable.Rows.Find(keyValues);
                        }
                        // row doesn't exist in target table, or there are no primary keys.
                        // create new row and copy values from source row to the new row.
                        if (targetRow == null)
                        { 
-                               targetRow = targetTable.NewRow();
-                               targetRow.Proposed = -1;
-                               row.CopyValuesToRow(targetRow);
-                               targetTable.Rows.Add(targetRow);
+                               DataRow newRow = targetTable.NewNotInitializedRow();
+                               // Don't check for ReadOnly, when cloning data to new uninitialized row.
+                               row.CopyValuesToRow(newRow, false);
+                               targetTable.Rows.AddInternal (newRow);
                        }
                        // row exists in target table, and presere changes is false - 
                        // change the values of the target row to the values of the source row.
-                       else if (!preserveChanges)
+                       else
                        {
-                               row.CopyValuesToRow(targetRow);
+                               row.MergeValuesToRow(targetRow, preserveChanges);
                        }
                }
                        
-
-               // adjust the dataset schema according to the missingschemaaction param
-               // (relations).
-               // return false if adjusting fails.
-               private static bool AdjustSchema(DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
+               private static bool AdjustSchemaRelations (DataSet targetSet, DataSet sourceSet, MissingSchemaAction missingSchemaAction)
                {
-                       if (missingSchemaAction == MissingSchemaAction.Add || missingSchemaAction == MissingSchemaAction.AddWithKey) {
-                               foreach (DataRelation relation in sourceSet.Relations) {
-                                       // TODO : add more precise condition (columns)
-                                       if (!targetSet.Relations.Contains(relation.RelationName)) {
-                                               DataTable targetTable = targetSet.Tables[relation.ParentColumns[0].Table.TableName];
-                                               DataColumn[] parentColumns = ResolveColumns(sourceSet,targetTable,relation.ParentColumns);
-                                               targetTable = targetSet.Tables[relation.ChildColumns[0].Table.TableName];
-                                               DataColumn[] childColumns = ResolveColumns(sourceSet,targetTable,relation.ChildColumns);
-                                               if (parentColumns != null && childColumns != null) {
-                                                       DataRelation newRelation = new DataRelation(relation.RelationName,parentColumns,childColumns);
-                                                       newRelation.Nested = relation.Nested; 
-                                                       targetSet.Relations.Add(newRelation);
-                                               }
+                       if (missingSchemaAction == MissingSchemaAction.Ignore)
+                               return true;
+
+                       foreach(DataTable sourceTable in sourceSet.Tables) {
+
+                               DataTable targetTable = targetSet.Tables[sourceTable.TableName];
+                               if (targetTable == null)
+                                       continue;
+
+                               foreach (Constraint constraint in sourceTable.Constraints) {
+
+                                       Constraint targetConstraint = null;
+
+                                       string constraintName = constraint.ConstraintName;
+                                       if (targetTable.Constraints.Contains (constraintName))
+                                               constraintName = "";
+
+                                       UniqueConstraint uc = constraint as UniqueConstraint;
+                                       // PrimaryKey is already taken care of while merging the table
+                                       // ForeignKey constraint takes care of Parent Unique Constraints
+                                       if (uc != null) {
+                                               if (uc.IsPrimaryKey || uc.ChildConstraint != null)
+                                                       continue;
+                                               DataColumn[] columns = ResolveColumns (targetTable, uc.Columns);
+                                               targetConstraint = new UniqueConstraint (constraintName, columns, false);
                                        }
-                                       else {
-                                               // TODO : should we throw an exeption ?
+
+                                       ForeignKeyConstraint fc = constraint as ForeignKeyConstraint;
+                                       if (fc != null) {
+                                               DataColumn[] columns = ResolveColumns (targetTable, fc.Columns);
+                                               DataColumn[] relatedColumns = ResolveColumns (targetSet.Tables [fc.RelatedTable.TableName],
+                                                                                       fc.RelatedColumns);
+                                               targetConstraint = new ForeignKeyConstraint (constraintName, relatedColumns, columns);
                                        }
-                               }                       
-
-                               foreach(DataTable sourceTable in sourceSet.Tables) {                            
-                                       DataTable targetTable = targetSet.Tables[sourceTable.TableName];
-
-                                       if (targetTable != null) {
-                                               foreach(Constraint constraint in sourceTable.Constraints) {
-
-                                                       if (constraint is UniqueConstraint) {
-                                                               UniqueConstraint uc = (UniqueConstraint)constraint;
-                                                               // FIXME : add more precise condition (columns)
-                                                               if ( !targetTable.Constraints.Contains(uc.ConstraintName) ) {           
-                                                                       DataColumn[] columns = ResolveColumns(sourceSet,targetTable,uc.Columns);
-                                                                       if (columns != null) {
-                                                                               UniqueConstraint newConstraint = new UniqueConstraint(uc.ConstraintName,columns,uc.IsPrimaryKey);
-                                                                               targetTable.Constraints.Add(newConstraint);
-                                                                       }
-                                                               }
-                                                               else {
-                                                                       // FIXME : should we throw an exception ?
-                                                               }
-                                                       }
-                                                       else {
-                                                               ForeignKeyConstraint fc = (ForeignKeyConstraint)constraint;
-                                                               // FIXME : add more precise condition (columns)
-                                                               if (!targetTable.Constraints.Contains(fc.ConstraintName)) {
-                                                                       DataColumn[] columns = ResolveColumns(sourceSet,targetTable,fc.Columns);
-                                                                       DataTable relatedTable = targetSet.Tables[fc.RelatedTable.TableName];
-                                                                       DataColumn[] relatedColumns = ResolveColumns(sourceSet,relatedTable,fc.RelatedColumns);
-                                                                       if (columns != null && relatedColumns != null) {
-                                                                               ForeignKeyConstraint newConstraint = new ForeignKeyConstraint(fc.ConstraintName,relatedColumns,columns);
-                                                                               targetTable.Constraints.Add(newConstraint);
-                                                                       }
-                                                               }
-                                                               else {
-                                                                       // FIXME : should we throw an exception ?
-                                                               }
-                                                       }
-                                               }
+
+                                       bool dupConstraintFound = false;
+                                       foreach (Constraint cons in targetTable.Constraints) {
+                                       if (!targetConstraint.Equals (cons))
+                                               continue;
+                                       dupConstraintFound = true;
+                                       break;
                                        }
+
+                                       // If equivalent-constraint already exists, then just do nothing
+                                       if (dupConstraintFound)
+                                               continue;
+
+                                       if (missingSchemaAction == MissingSchemaAction.Error)
+                                               throw new DataException ("Target DataSet missing " + targetConstraint.GetType () +
+                                                               targetConstraint.ConstraintName);
+                                       else
+                                               targetTable.Constraints.Add (targetConstraint);
+                               }
+                       }
+
+                       foreach (DataRelation relation in sourceSet.Relations) {
+                               DataRelation targetRelation = targetSet.Relations [relation.RelationName];
+                               if (targetRelation == null) {
+                                       if (missingSchemaAction == MissingSchemaAction.Error)
+                                               throw new ArgumentException ("Target DataSet mising definition for " +
+                                                               relation.RelationName);
+
+                                       DataColumn[] parentColumns = ResolveColumns (targetSet.Tables [relation.ParentTable.TableName],
+                                                       relation.ParentColumns);
+                                       DataColumn[] childColumns = ResolveColumns (targetSet.Tables [relation.ChildTable.TableName],
+                                                       relation.ChildColumns);
+                                       targetRelation = targetSet.Relations.Add (relation.RelationName, parentColumns,
+                                                       childColumns, relation.createConstraints);
+                                       targetRelation.Nested = relation.Nested;
+                               } else if (!CompareColumnArrays (relation.ParentColumns, targetRelation.ParentColumns) ||
+                                               !CompareColumnArrays (relation.ChildColumns, targetRelation.ChildColumns)) {
+                                       RaiseMergeFailedEvent (null, "Relation " + relation.RelationName +
+                                               " cannot be merged, because keys have mismatch columns.");
                                }
                        }
 
                        return true;
                }
 
-               private static DataColumn[] ResolveColumns(DataSet targetSet,DataTable targetTable,DataColumn[] sourceColumns)
+               private static DataColumn[] ResolveColumns(DataTable targetTable, DataColumn[] sourceColumns)
                {
                        if (sourceColumns != null && sourceColumns.Length > 0) {
-                               // TODO : worth to check that all source colums come from the same table
+                               // lets just assume that all columns are from the Same table
                                if (targetTable != null) {
                                        int i=0;
                                        DataColumn[] targetColumns = new DataColumn[sourceColumns.Length];
+
+                                       DataColumn tmpCol ;
                                        foreach(DataColumn sourceColumn in sourceColumns) {
-                                               targetColumns[i++] = targetTable.Columns[sourceColumn.ColumnName];
+                                               tmpCol = targetTable.Columns[sourceColumn.ColumnName];
+                                               if (tmpCol == null)
+                                                       throw new DataException ("Column " + sourceColumn.ColumnName  + 
+                                                                       " does not belong to table " + targetTable.TableName);
+                                               targetColumns [i++] = tmpCol;
                                        }
                                        return targetColumns;
                                }
@@ -230,62 +263,51 @@ namespace System.Data
                        return null;
                }
 
-               
+
                // adjust the table schema according to the missingschemaaction param.
                // return false if adjusting fails.
                private static bool AdjustSchema(DataSet targetSet, DataTable sourceTable, MissingSchemaAction missingSchemaAction, ref DataTable newTable)
                {
-                       string tableName = sourceTable.TableName;
-                       
                        // if the source table not exists in the target dataset
                        // we act according to the missingschemaaction param.
-                       int tmp = targetSet.Tables.IndexOf(tableName);
-                       // we need to check if it is equals names
-                       if (tmp != -1 && !targetSet.Tables[tmp].TableName.Equals(tableName))
-                               tmp = -1;
-                       if (tmp == -1) {
-                               if (missingSchemaAction == MissingSchemaAction.Ignore) {
+                       DataTable targetTable = targetSet.Tables [sourceTable.TableName];
+                       if (targetTable == null) {
+                               if (missingSchemaAction == MissingSchemaAction.Ignore)
                                        return true;
-                               }
-                               if (missingSchemaAction == MissingSchemaAction.Error) {
-                                       throw new ArgumentException("Target DataSet missing definition for "+ tableName + ".");
-                               }
-                               
-                               DataTable cloneTable = (DataTable)sourceTable.Clone();
-                               targetSet.Tables.Add(cloneTable);
-                               tableName = cloneTable.TableName;
-                       }                                                               
-                       
-                       DataTable table = targetSet.Tables[tableName];
-                       
+
+                               if (missingSchemaAction == MissingSchemaAction.Error)
+                                       throw new ArgumentException ("Target DataSet missing definition for " +
+                                                       sourceTable.TableName + ".");
+
+                               targetTable = (DataTable)sourceTable.Clone();
+                               targetSet.Tables.Add(targetTable);
+                       }
+
+                       AdjustSchema (targetTable, sourceTable, missingSchemaAction);
+
+                       newTable = targetTable;
+                       return true;
+               }
+
+
+               private static bool AdjustSchema(DataTable targetTable, DataTable sourceTable, MissingSchemaAction missingSchemaAction)
+               {
+                       if (missingSchemaAction == MissingSchemaAction.Ignore)
+                               return true;
+
                        for (int i = 0; i < sourceTable.Columns.Count; i++) {
                                DataColumn sourceColumn = sourceTable.Columns[i];
                                // if a column from the source table doesn't exists in the target table
                                // we act according to the missingschemaaction param.
-                               DataColumn targetColumn = table.Columns[sourceColumn.ColumnName];
+                               DataColumn targetColumn = targetTable.Columns [sourceColumn.ColumnName];
                                if(targetColumn == null) {
-                                       if (missingSchemaAction == MissingSchemaAction.Ignore) {
-                                               continue;
-                                       }
-                                       if (missingSchemaAction == MissingSchemaAction.Error) {
-                                               throw new ArgumentException(("Column '" + sourceColumn.ColumnName + "' does not belong to table Items."));
-                                       }
+                                       if (missingSchemaAction == MissingSchemaAction.Error)
+                                               throw new DataException ("Target table " + targetTable.TableName +
+                                                               " missing definition for column " + sourceColumn.ColumnName);
                                        
-                                       targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType, sourceColumn.Expression, sourceColumn.ColumnMapping);
-                                       table.Columns.Add(targetColumn);
-                               }
-
-                               if (sourceColumn.Unique) {
-                                       try {
-                                               targetColumn.Unique = sourceColumn.Unique;
-                                       }
-                                       catch(Exception e){
-//                                             Console.WriteLine("targetColumn : {0}   targetTable : {1} ",targetColumn.ColumnName,table.TableName);
-                                               foreach(DataRow row in table.Rows) {
-//                                                     Console.WriteLine(row[targetColumn]);
-                                               }
-                                               throw e;
-                                       }
+                                       targetColumn = new DataColumn(sourceColumn.ColumnName, sourceColumn.DataType, 
+                                                               sourceColumn.Expression, sourceColumn.ColumnMapping);
+                                       targetTable.Columns.Add(targetColumn);
                                }
 
                                if(sourceColumn.AutoIncrement) {
@@ -295,88 +317,47 @@ namespace System.Data
                                }
                        }
 
-                       if (!AdjustPrimaryKeys(table, sourceTable)) {
+                       if (!AdjustPrimaryKeys(targetTable, sourceTable))
                                return false;
-                       }
 
-                       newTable = table;
+                       checkColumnTypes (targetTable, sourceTable);
+
                        return true;
                }
-               
+       
+       
                // find if there is a valid matching between the targetTable PrimaryKey and the
                // sourceTable primatyKey.
                // return true if there is a match, else return false and raise a MergeFailedEvent.
                private static bool AdjustPrimaryKeys(DataTable targetTable, DataTable sourceTable)
                {
-                       // if the length of one of the tables primarykey if 0 - there is nothing to check.
-                       if (sourceTable.PrimaryKey.Length != 0) {
-                               if (targetTable.PrimaryKey.Length == 0) {
-                                       // if target table has no primary key at all - 
-                                       // import primary key from source table
-                                       DataColumn[] targetColumns = new DataColumn[sourceTable.PrimaryKey.Length];
-                                       
-                                       for(int i=0; i < sourceTable.PrimaryKey.Length; i++){
-                                           DataColumn sourceColumn = sourceTable.PrimaryKey[i];
-                                               DataColumn targetColumn = targetTable.Columns[sourceColumn.ColumnName];
-
-                                               if (targetColumn == null) {
-                                                       // is target table has no column corresponding
-                                                       // to source table PK column - merge fails
-                                                       string message = "Column " + sourceColumn.ColumnName + " does not belongs to table " + targetTable.TableName;
-                                                       MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
-                                                       targetTable.DataSet.OnMergeFailed(e);
-                                                       return false;
-                                               }
-                                               else {
-                                                       targetColumns[i] = targetColumn;
-                                               }
-                                       }
-                                       targetTable.PrimaryKey = targetColumns;
-                               }
-                               else {
-                                       // if target table already has primary key and
-                                       // if the length of primarykey is not equal - merge fails
-                                       if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
-                                               string message = "<target>.PrimaryKey and <source>.PrimaryKey have different Length.";
-                                               MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
-                                               targetTable.DataSet.OnMergeFailed(e);
-                                               return false;
-                                       }
-                                       else {
-                                               // we have to see that each primary column in the target table
-                                               // has a column with the same name in the sourcetable primarykey columns. 
-                                               bool foundMatch;
-                                               DataColumn[] targetDataColumns = targetTable.PrimaryKey;
-                                               DataColumn[] srcDataColumns = sourceTable.PrimaryKey;
-
-                                               // loop on all primary key columns in the targetTable.
-                                               for (int i = 0; i < targetDataColumns.Length; i++) {
-                                                       foundMatch = false;
-                                                       DataColumn col = targetDataColumns[i];
-
-                                                       // find if there is a column with the same name in the 
-                                                       // sourceTable primary key columns.
-                                                       for (int j = 0; j < srcDataColumns.Length; j++) {
-                                                               if (srcDataColumns[j].ColumnName == col.ColumnName) {
-                                                                       foundMatch = true;
-                                                                       break;
-                                                               }
-                                                       }
-                                                       if (!foundMatch) {
-                                                               string message = "Mismatch columns in the PrimaryKey : <target>." + col.ColumnName + " versus <source>." + srcDataColumns[i].ColumnName + ".";
-                                                               MergeFailedEventArgs e = new MergeFailedEventArgs(sourceTable, message);
-                                                               targetTable.DataSet.OnMergeFailed(e);
-                                                               return false;
-                                                       }
-                                                       
-                                               }
-                                       }
-                               }
+                       if (sourceTable.PrimaryKey.Length == 0)
+                               return true;
+
+                       // If targetTable does not have a PrimaryKey, just import the sourceTable PrimaryKey
+                       if (targetTable.PrimaryKey.Length == 0) {
+                               DataColumn[] targetColumns = ResolveColumns (targetTable, sourceTable.PrimaryKey);
+                               targetTable.PrimaryKey = targetColumns;
+                               return true;
+                       }
+                       
+                       // If both the tables have a primary key, verify that they are equivalent.
+                       // raise a MergeFailedEvent if the keys are not equivalent
+                       if (targetTable.PrimaryKey.Length != sourceTable.PrimaryKey.Length) {
+                               RaiseMergeFailedEvent (targetTable, "<target>.PrimaryKey and <source>.PrimaryKey have different Length.");
+                               return false;
                        }
 
+                       for (int i=0; i < targetTable.PrimaryKey.Length; ++i) {
+                               if (targetTable.PrimaryKey [i].ColumnName.Equals (sourceTable.PrimaryKey [i].ColumnName))
+                                       continue;
+                               RaiseMergeFailedEvent (targetTable, "Mismatch columns in the PrimaryKey : <target>." + 
+                                       targetTable.PrimaryKey [i].ColumnName + " versus <source>." + sourceTable.PrimaryKey [i].ColumnName);
+                               return false;
+                       }
                        return true;
                }
-               
+
                // fill the data from the source table to the target table
                private static void fillData(DataTable targetTable, DataTable sourceTable, bool preserveChanges)
                {
@@ -386,7 +367,7 @@ namespace System.Data
                                MergeRow(targetTable, row, preserveChanges);
                        }
                }
-               
+
                // check tha column from 2 tables that has the same name also has the same datatype.
                private static void checkColumnTypes(DataTable targetTable, DataTable sourceTable)
                {
@@ -394,9 +375,32 @@ namespace System.Data
                        {
                                DataColumn fromCol = sourceTable.Columns[i];
                                DataColumn toCol = targetTable.Columns[fromCol.ColumnName];
-                               if((toCol != null) && (toCol.DataType != fromCol.DataType))
-                                       throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + fromCol.ColumnName + " have conflicting properties: DataType property mismatch.");
+                               if (toCol == null)
+                                       continue;
+                               if (toCol.DataTypeMatches (fromCol))
+                                       continue;
+                               throw new DataException("<target>." + fromCol.ColumnName + " and <source>." + 
+                                               fromCol.ColumnName + " have conflicting properties: DataType " + 
+                                               " property mismatch.");
                        }
                }
+
+               private static bool CompareColumnArrays (DataColumn[] arr1, DataColumn[] arr2)
+               {
+                       if (arr1.Length != arr2.Length)
+                               return false;
+
+                       for (int i=0; i < arr1.Length; ++i)
+                               if (!arr1 [i].ColumnName.Equals (arr2 [i].ColumnName))
+                                       return false;
+                       return true;
+               }
+
+               private static void RaiseMergeFailedEvent (DataTable targetTable, string errMsg)
+               {
+                       MergeFailedEventArgs args = new MergeFailedEventArgs (targetTable, errMsg);
+                       if (targetTable.DataSet != null)
+                               targetTable.DataSet.OnMergeFailed (args);
+               }
        }
 }