internal DataRow MergeRow(DataRow row, DataRow targetRow, bool preserveChanges, Index idxSearch)
{
if (targetRow == null)
{
targetRow = NewEmptyRow();
targetRow._oldRecord = _recordManager.ImportRecord(row.Table, row._oldRecord);
targetRow._newRecord = targetRow._oldRecord;
if (row._oldRecord != row._newRecord)
{
targetRow._newRecord = _recordManager.ImportRecord(row.Table, row._newRecord);
}
InsertRow(targetRow, -1);
}
else
{
// Record Manager corruption during Merge when target row in edit state
// the newRecord would be freed and overwrite tempRecord (which became the newRecord)
// this would leave the DataRow referencing a freed record and leaking memory for the now lost record
int proposedRecord = targetRow._tempRecord; // by saving off the tempRecord, EndEdit won't free newRecord
targetRow._tempRecord = -1;
try
{
DataRowState saveRowState = targetRow.RowState;
int saveIdxRecord = (saveRowState == DataRowState.Added) ? targetRow._newRecord : saveIdxRecord = targetRow._oldRecord;
int newRecord;
int oldRecord;
if (targetRow.RowState == DataRowState.Unchanged && row.RowState == DataRowState.Unchanged)
{
// unchanged row merging with unchanged row
oldRecord = targetRow._oldRecord;
newRecord = (preserveChanges) ? _recordManager.CopyRecord(this, oldRecord, -1) : targetRow._newRecord;
oldRecord = _recordManager.CopyRecord(row.Table, row._oldRecord, targetRow._oldRecord);
SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
}
else if (row._newRecord == -1)
{
// Incoming row is deleted
oldRecord = targetRow._oldRecord;
if (preserveChanges)
{
newRecord = (targetRow.RowState == DataRowState.Unchanged) ? _recordManager.CopyRecord(this, oldRecord, -1) : targetRow._newRecord;
}
else
newRecord = -1;
oldRecord = _recordManager.CopyRecord(row.Table, row._oldRecord, oldRecord);
// Change index record, need to update index
if (saveIdxRecord != ((saveRowState == DataRowState.Added) ? newRecord : oldRecord))
{
SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
idxSearch.Reset();
saveIdxRecord = ((saveRowState == DataRowState.Added) ? newRecord : oldRecord);
}
else
{
SetMergeRecords(targetRow, newRecord, oldRecord, (newRecord == -1) ? DataRowAction.Delete : DataRowAction.Change);
}
}
else
{
// incoming row is added, modified or unchanged (targetRow is not unchanged)
oldRecord = targetRow._oldRecord;
newRecord = targetRow._newRecord;
if (targetRow.RowState == DataRowState.Unchanged)
{
newRecord = _recordManager.CopyRecord(this, oldRecord, -1);
}
oldRecord = _recordManager.CopyRecord(row.Table, row._oldRecord, oldRecord);
if (!preserveChanges)
{
newRecord = _recordManager.CopyRecord(row.Table, row._newRecord, newRecord);
}
SetMergeRecords(targetRow, newRecord, oldRecord, DataRowAction.Change);
}
if (saveRowState == DataRowState.Added && targetRow._oldRecord != -1)
{
idxSearch.Reset();
}
Debug.Assert(saveIdxRecord == ((saveRowState == DataRowState.Added) ? targetRow._newRecord : targetRow._oldRecord), "oops, you change index record without noticing it");
}
finally
{
targetRow._tempRecord = proposedRecord;
}
}
// Merge all errors
if (row.HasErrors)
{
if (targetRow.RowError.Length == 0)
{
targetRow.RowError = row.RowError;
}
else
{
targetRow.RowError += " ]:[ " + row.RowError;
}
DataColumn[] cols = row.GetColumnsInError();
for (int i = 0; i < cols.Length; i++)
{
DataColumn col = targetRow.Table.Columns[cols[i].ColumnName];
targetRow.SetColumnError(col, row.GetColumnError(cols[i]));
}
}
else
{
if (!preserveChanges)
{
targetRow.ClearErrors();
}
}
return targetRow;
}