+2005-04-22 Sureshkumar T <tsureshkumar@novell.com>
+
+ * System.Data_test.dll.sources: Added DataTableLoadRowTest.cs.
+
2005-04-08 Raja R Harinath <rharinath@novell.com>
* Makefile (EXTRA_DISTFILES): Add app_test_2.0.config.
+2005-04-22 Sureshkumar T <tsureshkumar@novell.com>
+
+ * DbDataAdapter.cs: Moved FillTable and BuildSchema as static
+ methods as they are not operating on the current instance. This
+ could be reused to fill any table from a data reader.
+
+ * RecordCache.cs: While disposing records, make sure that the
+ record is not already disposed. i.e. to make sure later the same
+ record is not reused. Implemented a static method to compare two
+ version of records in a container.
+
2005-04-18 Sureshkumar T <tsureshkumar@novell.com>
* DataAdapter.cs: Implemenetd OnFillError handler.
// Author:
// Rodrigo Moya (rodrigo@ximian.com)
// Tim Coleman (tim@timcoleman.com)
+// Sureshkumar T <tsureshkumar@novell.com>
//
// (C) Ximian, Inc
// Copyright (C) Tim Coleman, 2002-2003
return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
}
- private bool FillTable (DataTable dataTable, IDataReader dataReader, int startRecord, int maxRecords, ref int counter)
+ private bool FillTable (DataTable dataTable, IDataReader dataReader, int startRecord, int maxRecords, ref int counter)
{
- if (dataReader.FieldCount == 0) {
+ if (dataReader.FieldCount == 0)
return false;
- }
- int counterStart = counter;
-
int[] mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
-
- for (int i = 0; i < startRecord; i++) {
- dataReader.Read ();
- }
-
- while (dataReader.Read () && (maxRecords == 0 || (counter - counterStart) < maxRecords)) {
- try {
- dataTable.BeginLoadData ();
- dataTable.LoadDataRow (dataReader, mapping, AcceptChangesDuringFill);
- dataTable.EndLoadData ();
- counter++;
- }
- catch (Exception e) {
- object[] readerArray = new object[dataReader.FieldCount];
- object[] tableArray = new object[dataReader.FieldCount];
- // we get the values from the datareader
- dataReader.GetValues (readerArray);
- // copy from datareader columns to table columns according to given mapping
- int count = 0;
- for (int i = 0; i < dataTable.Columns.Count; i++) {
- if (mapping [i] >= 0)
- tableArray[count++] = readerArray[mapping[i]];
- }
- FillErrorEventArgs args = CreateFillErrorEvent (dataTable, tableArray, e);
- OnFillError (args);
- if(!args.Continue) {
- return false;
- }
- }
- }
+ try {
+ FillTable (dataTable, dataReader, startRecord, maxRecords, mapping,
+ AcceptChangesDuringFill, ref counter);
+ } catch (Exception e) {
+ object[] readerArray = new object[dataReader.FieldCount];
+ object[] tableArray = new object[dataReader.FieldCount];
+ // we get the values from the datareader
+ dataReader.GetValues (readerArray);
+ // copy from datareader columns to table columns according to given mapping
+ int count = 0;
+ for (int i = 0; i < dataTable.Columns.Count; i++) {
+ if (mapping [i] >= 0)
+ tableArray[count++] = readerArray[mapping[i]];
+ }
+ FillErrorEventArgs args = CreateFillErrorEvent (dataTable, tableArray, e);
+ OnFillError (args);
+ if(!args.Continue) {
+ return false;
+ }
+ }
return true;
}
+ /// <summary>
+ /// Fills the given datatable using values from reader. if a column
+ /// does not have a mapped reader column (-1 in mapping), that will
+ /// be filled with default value.
+ /// </summary>
+ internal static void FillTable (DataTable dataTable,
+ IDataReader dataReader,
+ int startRecord,
+ int maxRecords,
+ int [] mapping,
+ bool acceptChanges,
+ ref int counter)
+ {
+ if (dataReader.FieldCount == 0)
+ return ;
+
+ for (int i = 0; i < startRecord; i++)
+ dataReader.Read ();
+
+ int counterStart = counter;
+ while (dataReader.Read () && (maxRecords == 0 || (counter - counterStart) < maxRecords)) {
+ dataTable.BeginLoadData ();
+ dataTable.LoadDataRow (dataReader, mapping, acceptChanges);
+ dataTable.EndLoadData ();
+ counter++;
+ }
+ }
+#if NET_2_0
+ /// <summary>
+ /// Fills the given datatable using values from reader. if a value
+ /// for a column is null, that will be filled with default value.
+ /// </summary>
+ /// <returns>No. of rows affected </returns>
+ internal static int FillFromReader (DataTable table,
+ IDataReader reader,
+ int start,
+ int length,
+ int [] mapping,
+ LoadOption loadOption
+ )
+ {
+ if (reader.FieldCount == 0)
+ return 0 ;
+
+ for (int i = 0; i < start; i++)
+ reader.Read ();
+
+ int counter = 0;
+ object [] values = new object [mapping.Length];
+ while (reader.Read () &&
+ (length == 0 || counter < length)) {
+
+ for (int i = 0 ; i < mapping.Length; i++)
+ values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
+
+ table.BeginLoadData ();
+ table.LoadDataRow (values, loadOption);
+ table.EndLoadData ();
+ counter++;
+ }
+ return counter;
+ }
+
+#endif // NET_2_0
+
public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType)
{
return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
// the int array represents this match.
[MonoTODO ("Test")]
private int[] BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
+ {
+ return BuildSchema (reader, table, schemaType, MissingSchemaAction,
+ MissingMappingAction, TableMappings);
+ }
+
+ /// <summary>
+ /// Creates or Modifies the schema of the given DataTable based on the schema of
+ /// the reader and the arguments passed.
+ /// </summary>
+ internal static int[] BuildSchema (IDataReader reader,
+ DataTable table,
+ SchemaType schemaType,
+ MissingSchemaAction missingSchAction,
+ MissingMappingAction missingMapAction,
+ DataTableMappingCollection dtMapping
+ )
{
int readerIndex = 0;
int[] mapping = new int[reader.FieldCount + table.Columns.Count]; // mapping the reader indexes to the datatable indexes
// generate DataSetColumnName from DataTableMapping, if any
string dsColumnName = realSourceColumnName;
DataTableMapping tableMapping = null;
- tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, table.TableName, table.TableName, MissingMappingAction);
+ tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (dtMapping, table.TableName, table.TableName, missingMapAction);
if (tableMapping != null)
{
table.TableName = tableMapping.DataSetTable;
// check to see if the column mapping exists
- DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, MissingMappingAction);
+ DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, missingMapAction);
if (columnMapping != null)
{
DataColumn col =
columnMapping.GetDataColumnBySchemaAction(
table ,
(Type)schemaRow["DataType"],
- MissingSchemaAction);
+ missingSchAction);
if (col != null)
{
// if the column is not in the table - add it.
if (table.Columns.IndexOf(col) == -1)
{
- if (MissingSchemaAction == MissingSchemaAction.Add
- || MissingSchemaAction == MissingSchemaAction.AddWithKey)
+ if (missingSchAction == MissingSchemaAction.Add
+ || missingSchAction == MissingSchemaAction.AddWithKey)
table.Columns.Add(col);
}
-
- if (MissingSchemaAction == MissingSchemaAction.AddWithKey) {
+ if (missingSchAction == MissingSchemaAction.AddWithKey) {
if (!schemaRow["IsKey"].Equals (DBNull.Value))
if ((bool) (schemaRow ["IsKey"]))
primaryKey.Add (col);
table.PrimaryKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
return mapping;
- }
+
+ }
[MonoTODO]
object ICloneable.Clone ()
internal int NewRecord()
{
if (_records.Count > 0) {
- return (int)_records.Pop();
+ return (int)_records.Pop();
}
else {
DataColumnCollection cols = _table.Columns;
if ( index < 0 ) {
throw new ArgumentException();
}
- _records.Push(index);
+ if (! _records.Contains (index)) {
+ _records.Push(index);
+ }
}
internal int CopyRecord(DataTable fromTable,int fromRecordIndex,int toRecordIndex)
return recordIndex;
}
+ /// <summary>
+ /// Compares two records in the given data table. The numbers are the offset
+ /// into the container tables.
+ /// </summary>
+ internal static bool CompareRecords (DataTable table, int x, int y)
+ {
+ foreach (DataColumn dc in table.Columns) {
+ if (dc.DataContainer.CompareValues (x, y) != 0)
+ return false;
+ }
+ return true;
+ }
+
#endregion // Methods
}
+2005-04-22 Sureshkumar T <tsureshkumar@novell.com>
+
+ * LoadOption.cs: Changed the enums. Keeping old values for
+ migration.
+
+ * DataTable.cs: Implemented methods Load and its overloads. Also
+ implemented LoadDataRow.
+
+ * DataRowCollection.cs: Added a variation of the Find method to
+ return a row even if it is of state Deleted. This is required for
+ DataTable.LoadDataRow method.
+
+ * DataRow.cs: Implemented internal helper method Load for loading
+ values from an object array and given an LoadOption (2.0 feature).
+
+ * DataRowAction.cs: Added few more enums for .net 2.0. Sorted
+ alphabetically.
+
2005-04-20 Jordi Mas i Hernandez <jordi@ximian.com>
* DataViewManager.cs: implements TypedList.GetListName used in SWF
// Tim Coleman <tim@timcoleman.com>
// Ville Palo <vi64pa@koti.soon.fi>
// Alan Tam Siu Lung <Tam@SiuLung.com>
+// Sureshkumar T <tsureshkumar@novell.com>
//
// (C) Ximian, Inc 2002
// (C) Daniel Morgan 2002, 2003
//
using System;
+using System.Data.Common;
using System.Collections;
using System.Globalization;
using System.Xml;
}
}
+ /// <summary>
+ /// Sets the index into the container records for the original version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ /// </summary>
+ internal int Original
+ {
+ get { return _original;}
+ set {
+ if (_original == value)
+ return;
+
+ if (_original >= 0
+ && _current != _original
+ && _proposed != _original)
+ Table.RecordCache.DisposeRecord (_original);
+ _original = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets the index into the container records for the proposed version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ internal int Proposed
+ {
+ get { return _proposed;}
+ set {
+ if (_proposed == value)
+ return;
+ if (_proposed >= 0
+ && _proposed != _current
+ && _proposed != _original)
+ Table.RecordCache.DisposeRecord (_proposed);
+ _proposed = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets the index into the container records for the current version. Apart
+ /// from that, it makes sure it pools the record used earlier if they are not
+ /// used by other versions.
+ internal int Current
+ {
+ get { return _current;}
+ set {
+ if (_current == value)
+ return;
+ if (_current >= 0
+ && _current != _original
+ && _current != _proposed)
+ Table.RecordCache.DisposeRecord (_current);
+ _current = value;
+ }
+ }
+
+ /// <summary>
+ /// Set a value for the column into the offset specified by the version.<br>
+ /// If the value is auto increment or null, necessary auto increment value
+ /// or the default value will be used.
+ /// </summary>
+ internal void SetValue (int column, object value, int version)
+ {
+ DataColumn dc = Table.Columns[column];
+
+ if (value == null && ! dc.AutoIncrement) // set default value / auto increment
+ value = dc.DefaultValue;
+
+ Table.ChangingDataColumn (this, dc, value);
+ CheckValue (value, dc);
+ if ( ! dc.AutoIncrement)
+ dc [version] = value;
+ else if (_proposed >= 0 && _proposed != version) // proposed holds the AI
+ dc [version] = dc [_proposed];
+ }
+
/// <summary>
/// Gets the data stored in the column, specified by index and version of the data to
/// retrieve.
}
}
- #endregion
+ #endregion // Properties
#region Methods
throw new RowNotInTableException("Cannot perform this operation on a row not in the table.");
}
// Accept from detached
- if (_original >= 0) {
- Table.RecordCache.DisposeRecord(_original);
- }
- _original = _current;
+ if (_original != _current)
+ Original = Current;
}
/// <summary>
/// </summary>
public bool HasVersion (DataRowVersion version)
{
- switch (version) {
- case DataRowVersion.Default:
- if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
- return false;
- if (rowState == DataRowState.Detached)
- return _proposed >= 0;
- return true;
- case DataRowVersion.Proposed:
- if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
- return false;
- return _proposed >= 0;
- case DataRowVersion.Current:
- if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
- return false;
- return _current >= 0;
- case DataRowVersion.Original:
- if (rowState == DataRowState.Detached)
- return false;
- return _original >= 0;
- }
+ switch (version) {
+ case DataRowVersion.Default:
+ if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+ return false;
+ if (rowState == DataRowState.Detached)
+ return _proposed >= 0;
+ return true;
+ case DataRowVersion.Proposed:
+ if (rowState == DataRowState.Deleted && !_inExpressionEvaluation)
+ return false;
+ return _proposed >= 0;
+ case DataRowVersion.Current:
+ if ((rowState == DataRowState.Deleted && !_inExpressionEvaluation) || rowState == DataRowState.Detached)
+ return false;
+ return _current >= 0;
+ case DataRowVersion.Original:
+ if (rowState == DataRowState.Detached)
+ return false;
+ return _original >= 0;
+ }
return false;
}
}
#endregion // Methods
+
+#if NET_2_0
+ /// <summary>
+ /// This method loads a given value into the existing row affecting versions,
+ /// state based on the LoadOption. The matrix of changes for this method are as
+ /// mentioned in the DataTable.Load (IDataReader, LoadOption) method.
+ /// </summary>
+ [MonoTODO ("Raise necessary Events")]
+ internal void Load (object [] values, LoadOption loadOption, bool is_new)
+ {
+ DataRowAction action = DataRowAction.Change;
+
+ int temp = Table.RecordCache.NewRecord ();
+ for (int i = 0 ; i < Table.Columns.Count; i++)
+ SetValue (i, values [i], temp);
+
+ if (is_new) { // new row
+ if (editing || RowState == DataRowState.Detached)
+ Proposed = temp;
+ else
+ Current = temp;
+ return;
+ }
+
+ if (loadOption == LoadOption.OverwriteChanges
+ || (loadOption == LoadOption.PreserveChanges
+ && rowState == DataRowState.Unchanged)) {
+ Original = temp;
+ if (editing)
+ Proposed = temp;
+ else
+ Current = temp;
+ rowState = DataRowState.Unchanged;
+ action = DataRowAction.ChangeCurrentAndOriginal;
+ return;
+ }
+
+ if (loadOption == LoadOption.PreserveChanges) {
+ if (rowState != DataRowState.Deleted) {
+ Original = temp;
+ rowState = DataRowState.Modified;
+ action = DataRowAction.ChangeOriginal;
+ }
+ return;
+ }
+
+ bool not_used = true;
+ // Upsert
+ if (rowState != DataRowState.Deleted) {
+ int index = editing ? _proposed : _current;
+ if (! RecordCache.CompareRecords (Table, index, temp)) {
+ if (editing)
+ Proposed = temp;
+ else
+ Current = temp;
+ not_used = false;
+ if (rowState == DataRowState.Unchanged)
+ rowState = DataRowState.Modified;
+ }
+ }
+
+ if (not_used)
+ Table.RecordCache.DisposeRecord (temp);
+ }
+#endif // NET_2_0
}
//
// Author:
// Christopher Podurgiel (cpodurgiel@msn.com)
+// Sureshkumar T <tsureshkumar@novell.com>
//
// (C) Chris Podurgiel
//
[Serializable]
public enum DataRowAction
{
- Nothing = 0,
- Delete = 1,
- Change = 2,
- Rollback = 4,
- Commit = 8,
- Add = 16
+ Add = 16,
+ Change = 2,
+#if NET_2_0
+ ChangeCurrentAndOriginal = 64,
+ ChangeOriginal = 32,
+#endif // NET_2_0
+ Commit = 8,
+ Delete = 1,
+ Nothing = 0,
+ Rollback = 4
}
-
-}
\ No newline at end of file
+}
/// </summary>
public DataRow Find (object key)
{
- if (table.PrimaryKey.Length == 0)
+ return Find (key, // primary key value
+ true // ignore deleted records
+ );
+ }
+
+ /// <summary>
+ /// Searches records for the given primary key values.
+ /// </summary>
+ public DataRow Find (object [] keys)
+ {
+ return Find (keys, // primary key values
+ true // ignore deleted records
+ );
+ }
+
+ /// <summary>
+ /// Searches records for the given primary key values populated into
+ /// a temporary cache index.
+ /// </summary>
+ internal DataRow Find (int index, int length)
+ {
+ return Find (index, // record to find
+ length, // length of primary key
+ true // ignore deleted records
+ );
+ }
+
+ /// <summary>
+ /// Searches records for the given single primary key.
+ /// </summary>
+ /// <param name='key'>Primary key value to be searched </param>
+ /// <param name='ignoreDeleted'>
+ /// Ignore the records with row state DataRowState.Deleted
+ /// if true.
+ /// </param>
+ internal DataRow Find (object key, bool ignoreDeleted)
+ {
+ if (table.PrimaryKey.Length == 0)
throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
if (table.PrimaryKey.Length > 1)
throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received 1 value(s).");
//loop through all collection rows
foreach (DataRow row in this) {
- if (row.RowState != DataRowState.Deleted) {
- int index = row.IndexFromVersion(DataRowVersion.Default);
- if (primaryKey.DataContainer.CompareValues(index, tmpRecord) == 0) {
- return row;
- }
+ if (ignoreDeleted && row.RowState == DataRowState.Deleted)
+ continue;
+ int index = row.IndexFromVersion(DataRowVersion.Default);
+ if (row.RowState == DataRowState.Deleted)
+ index = row.Current;
+ if (primaryKey.DataContainer.CompareValues(index, tmpRecord) == 0) {
+ return row;
}
}
return null;
finally {
table.RecordCache.DisposeRecord(tmpRecord);
}
- }
+ }
- /// <summary>
- /// Gets the row containing the specified primary key values.
- /// </summary>
- public DataRow Find (object[] keys)
+ /// <summary>
+ /// Searches records for the given primary key values.
+ /// </summary>
+ /// <param name='keys'>Primary key values to be searched </param>
+ /// <param name='ignoreDeleted'>
+ /// Ignore the records with row state DataRowState.Deleted
+ /// if true.
+ /// </param>
+ internal DataRow Find (object[] keys, bool ignoreDeleted)
{
- if (table.PrimaryKey.Length == 0)
- throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
+ AssertFind (keys);
- if (keys == null)
- throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received 0 value(s).");
- if (table.PrimaryKey.Length != keys.Length)
- throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received " + keys.Length + " value(s).");
-
DataColumn[] primaryKey = table.PrimaryKey;
int tmpRecord = table.RecordCache.NewRecord();
try {
// according to MSDN: the DataType value for both columns must be identical.
primaryKey[i].DataContainer[tmpRecord] = keys[i];
}
- return Find(tmpRecord,numColumn);
+ return Find(tmpRecord, numColumn, ignoreDeleted);
}
finally {
table.RecordCache.DisposeRecord(tmpRecord);
}
}
- internal DataRow Find(int index, int length)
+ /// <summary>
+ /// Searches records for the given primary key values and the
+ /// given version.
+ /// </summary>
+ /// <param name='keys'>Primary key values to be searched </param>
+ /// <param name='version'>
+ /// Version of the rows to be searched for.
+ /// </param>
+ internal DataRow Find (object [] values, DataRowVersion version)
+ {
+ AssertFind (values);
+
+ DataColumn[] pk = table.PrimaryKey;
+ int temp = table.RecordCache.NewRecord();
+ try {
+ for (int i = 0; i < pk.Length; i++)
+ pk [i].DataContainer[temp] = values [i];
+ return Find(temp, version, false); // include deleted records also
+ }
+ finally {
+ table.RecordCache.DisposeRecord(temp);
+ }
+ }
+
+ /// <summary>
+ /// Searches records for the given primary key values and the
+ /// given version.
+ /// </summary>
+ /// <param name='record'>index of the record which holds the values to be searchd.
+ /// </param>
+ /// <param name='version'>
+ /// Version of the rows to be searched for.
+ /// </param>
+ /// <param name='ignoreDeleted'>Ignore the records with state Deleted </param>
+ private DataRow Find(int record, DataRowVersion version, bool ignoreDeleted)
{
- DataColumn[] primaryKey = table.PrimaryKey;
- Index primaryKeyIndex = table.GetIndexByColumns(primaryKey);
- // if we can search through index
- if (primaryKeyIndex != null) {
- // get the child rows from the index
- Node node = primaryKeyIndex.FindSimple(index,length,true);
- if ( node != null ) {
- return node.Row;
- }
+ DataRow resultRow = null;
+ if (version == DataRowVersion.Current) {
+ // index engine holds only the current records.
+ resultRow = IndexSearch (record);
+ if (resultRow != null)
+ return resultRow;
+ }
+
+ // fallback : loop through all collection rows
+ // if there is a matching record with state deleted, that won't be detected
+ // in the above search. (deleted records are not part of index.
+ foreach (DataRow row in this) {
+ if (ignoreDeleted && row.RowState != DataRowState.Deleted)
+ continue;
+
+ int offset = row.IndexFromVersion(version);
+ if (offset < 0)
+ continue;
+ bool matching = true;
+ for (int i = 0; matching && i < table.PrimaryKey.Length; i++) {
+ if (table.PrimaryKey [i].DataContainer.CompareValues(offset, record) != 0)
+ matching = false;
+ }
+ if (matching) {
+ resultRow = row;
+ break;
+ }
}
-
+ return resultRow;
+
+ }
+
+ /// <summary>
+ /// Searches records for the given primary key values and the
+ /// given version.
+ /// </summary>
+ /// <param name='index'> index of the record which holds the values to be searchd.
+ /// </param>
+ /// <param name='length'> length of primary keys </param>
+ /// <param name='ignoreDeleted'> Ignore the records with state Deleted </param>
+ private DataRow Find(int index, int length, bool ignoreDeleted)
+ {
+ DataRow resultRow = IndexSearch (index);
+ if (resultRow != null)
+ return resultRow;
+
//loop through all collection rows
foreach (DataRow row in this) {
- if (row.RowState != DataRowState.Deleted) {
- int rowIndex = row.IndexFromVersion(DataRowVersion.Default);
- bool match = true;
- for (int columnCnt = 0; columnCnt < length; ++columnCnt) {
- if (primaryKey[columnCnt].DataContainer.CompareValues(rowIndex, index) != 0) {
- match = false;
- }
- }
- if ( match ) {
- return row;
- }
- }
+ if (ignoreDeleted && row.RowState != DataRowState.Deleted)
+ continue;
+
+ int rowIndex = row.IndexFromVersion(DataRowVersion.Default);
+ if (row.RowState == DataRowState.Deleted)
+ rowIndex = row.Current;
+ bool match = true;
+ for (int columnCnt = 0; columnCnt < length; ++columnCnt) {
+ if (table.PrimaryKey[columnCnt].DataContainer.CompareValues(rowIndex, index) != 0) {
+ match = false;
+ }
+ }
+ if ( match ) {
+ return row;
+ }
}
return null;
}
+
+ /// <summary>
+ /// Asserts the validity of the search.
+ /// </summary>
+ private void AssertFind (object [] values)
+ {
+ if (table.PrimaryKey.Length == 0)
+ throw new MissingPrimaryKeyException ("Table doesn't have a primary key.");
+
+ if (values == null)
+ throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received 0 value(s).");
+
+ if (table.PrimaryKey.Length != values.Length)
+ throw new ArgumentException ("Expecting " + table.PrimaryKey.Length +" value(s) for the key being indexed, but received " + values.Length + " value(s).");
+
+ }
+
+ /// <summary>
+ /// Search for the record using the index.
+ /// </summary>
+ private DataRow IndexSearch (int record)
+ {
+ Index index = table.GetIndexByColumns(table.PrimaryKey);
+ // search in index
+ // priority is the non-deleted recrod
+ if (index != null) {
+ // get the child rows from the index
+ Node node = index.FindSimple(record,
+ table.PrimaryKey.Length,
+ true
+ );
+ if (node != null)
+ return node.Row;
+ }
+ return null;
+ }
+
/// <summary>
/// Inserts a new row into the collection at the specified location.
/// </summary>
// Rodrigo Moya <rodrigo@ximian.com>
// Tim Coleman (tim@timcoleman.com)
// Ville Palo <vi64pa@koti.soon.fi>
+// Sureshkumar T <tsureshkumar@novell.com>
//
// (C) Chris Podurgiel
// (C) Ximian, Inc 2002
}
#if NET_2_0
- [MonoTODO]
+ /// <summary>
+ /// Loads the table with the values from the reader
+ /// </summary>
public void Load (IDataReader reader)
{
- throw new NotImplementedException ();
+ Load (reader, LoadOption.PreserveChanges);
}
- [MonoTODO]
+ /// <summary>
+ /// Loads the table with the values from the reader and the pattern
+ /// of the changes to the existing rows or the new rows are based on
+ /// the LoadOption passed.
+ /// </summary>
public void Load (IDataReader reader, LoadOption loadOption)
{
- throw new NotImplementedException ();
- }
+ bool prevEnforceConstr = this.EnforceConstraints;
+ try {
+ this.EnforceConstraints = false;
+ int [] mapping = DbDataAdapter.BuildSchema (reader, this, SchemaType.Mapped,
+ MissingSchemaAction.AddWithKey,
+ MissingMappingAction.Passthrough,
+ new DataTableMappingCollection ());
+ DbDataAdapter.FillFromReader (this,
+ reader,
+ 0, // start from
+ 0, // all records
+ mapping,
+ loadOption);
+ } finally {
+ this.EnforceConstraints = prevEnforceConstr;
+ }
+ }
+
+
#endif
/// <summary>
for (int i = 0; i < PrimaryKey.Length && hasPrimaryValues; i++) {
DataColumn primaryKeyColumn = PrimaryKey[i];
int ordinal = primaryKeyColumn.Ordinal;
- if(ordinal < mapping.Length) {
+ if(mapping [ordinal] >= 0) {
primaryKeyColumn.DataContainer.SetItemFromDataRecord(tmpRecord,record,mapping[ordinal]);
}
else {
if (hasPrimaryValues) {
// find the row in the table.
- row = Rows.Find(tmpRecord,PrimaryKey.Length);
+ row = Rows.Find(tmpRecord, PrimaryKey.Length);
}
}
finally {
}
#if NET_2_0
- [MonoTODO]
- public DataRow LoadDataRow (object[] values, LoadOption loadOption)
- {
- throw new NotImplementedException ();
+ /// <summary>
+ /// Loads the given values into an existing row if matches or creates
+ /// the new row popluated with the values.
+ /// </summary>
+ /// <remarks>
+ /// This method searches for the values using primary keys and it first
+ /// searches using the original values of the rows and if not found, it
+ /// searches using the current version of the row.
+ /// </remarks>
+ public DataRow LoadDataRow (object [] values, LoadOption loadOption)
+ {
+ DataRow row = null;
+ bool new_row = false;
+
+ // Find Data DataRow
+ if (this.PrimaryKey.Length > 0) {
+ object [] keyValues = new object [this.PrimaryKey.Length];
+ for (int i = 0; i < keyValues.Length; i++)
+ keyValues [i] = values [this.PrimaryKey [i].Ordinal];
+ row = this.Rows.Find (keyValues, DataRowVersion.Original );
+ if (row == null)
+ row = this.Rows.Find (keyValues, DataRowVersion.Current);
+ }
+
+ // If not found, add new row
+ if (row == null) {
+ row = this.NewRow ();
+ new_row = true;
+ }
+
+ bool deleted = row.RowState == DataRowState.Deleted;
+
+ if (deleted && loadOption == LoadOption.OverwriteChanges)
+ row.RejectChanges ();
+
+ row.Load (values, loadOption, new_row);
+
+ if (deleted && loadOption == LoadOption.Upsert) {
+ row = this.NewRow ();
+ row.Load (values, loadOption, new_row = true);
+ }
+
+ if (new_row) {
+ this.Rows.Add (row);
+ if (loadOption == LoadOption.OverwriteChanges ||
+ loadOption == LoadOption.PreserveChanges) {
+ row.AcceptChanges ();
+ }
+ }
+
+ return row;
}
[MonoTODO]
//
// Author:
// Tim Coleman (tim@timcoleman.com)
+// Sureshkumar T <tsureshkumar@novell.com>
//
// Copyright (C) Tim Coleman, 2003
//
namespace System.Data {
public enum LoadOption
{
- OverwriteRow,
- PreserveCurrentValues,
- UpdateCurrentValues
+ OverwriteChanges = 2,
+ PreserveChanges = 3,
+ Upsert = 1,
+ [Obsolete ("Use OverwriteChanges insted")]
+ OverwriteRow = 2,
+ [Obsolete ("Use PreserveChanges insted")]
+ PreserveCurrentValues = 3,
+ [Obsolete ("Use Upsert insted")]
+ UpdateCurrentValues = 1
}
}
System.Data/DataSetReadXmlSchemaTest.cs
System.Data/DataSetInferXmlSchemaTest.cs
System.Data/DataTableTest.cs
+System.Data/DataTableLoadRowTest.cs
System.Data/DataViewManagerTest.cs
System.Data/DataViewTest.cs
System.Data/ForeignKeyConstraintTest.cs
+2005-04-22 Sureshkumar T <tsureshkumar@novell.com>
+
+ * DataTableLoadRowTest.cs: Added. A test case for testing
+ LoadDataRow method of DataTable. This tests for various
+ possiblities of row state and loadoption.
+
2005-04-19 Atsushi Enomoto <atsushi@ximian.com>
* DataViewTest.cs : added more RowStateFilter test (bug #74650).
--- /dev/null
+//
+// DataTableLoadRowTest.cs - NUnit Test Cases for testing the
+// DataTable's LoadRow method
+// Author:
+// Sureshkumar T (tsureshkumar@novell.com)
+//
+// Copyright (c) 2004 Novell Inc., and the individuals listed
+// on the ChangeLog entries.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_2_0
+
+using System;
+using System.Data;
+using System.Data.SqlClient;
+using System.Text;
+
+using NUnit.Framework;
+
+namespace MonoTests.System.Data.SqlClient
+{
+ [TestFixture]
+ public class DataTableLoadRowTest
+ {
+ [Test]
+ public void LoadRowTest ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Rows.Add (new object [] { 1, "mono 1" });
+ dt.Rows.Add (new object [] { 2, "mono 2" });
+ dt.Rows.Add (new object [] { 3, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+ dt.AcceptChanges ();
+
+ dt.LoadDataRow (new object [] { 4, "mono 4" }, LoadOption.Upsert);
+ Assert.AreEqual (4, dt.Rows.Count, "#1 has not added a new row");
+ }
+
+ [Test]
+ public void LoadRowTestUpsert ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Rows.Add (new object [] { 1, "mono 1" });
+ dt.Rows.Add (new object [] { 2, "mono 2" });
+ dt.Rows.Add (new object [] { 3, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+
+ dt.AcceptChanges ();
+
+ dt.LoadDataRow (new object [] { 2, "mono test" }, LoadOption.Upsert);
+ Assert.AreEqual (3, dt.Rows.Count, "#1 should not add a row");
+ Assert.AreEqual ("mono test", dt.Rows [1] [1], "#2 should change the current");
+ Assert.AreEqual ("mono 2", dt.Rows [1] [1, DataRowVersion.Original], "#3 should not change original");
+ Assert.AreEqual (DataRowState.Modified, dt.Rows [1].RowState, "#4 should change state");
+
+
+ // Row State tests
+ // current - modified ; result - modified
+ dt.LoadDataRow (new object [] { 2, "mono test 2" }, LoadOption.Upsert);
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1], "#c1 should change the current");
+ Assert.AreEqual ("mono 2", dt.Rows [1] [1, DataRowVersion.Original], "#c2 should not change original");
+ Assert.AreEqual (DataRowState.Modified, dt.Rows [1].RowState, "#c3 should not change state");
+
+ // current - Unchanged; result - Unchanged if no new value
+ dt.AcceptChanges ();
+ dt.LoadDataRow (new object [] { 2, "mono test 2" }, LoadOption.Upsert);
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1], "#c4 should change the current");
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1, DataRowVersion.Original], "#c5 should not change original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [1].RowState, "#c6 should not change state");
+ // not the same value again
+ dt.RejectChanges ();
+ dt.LoadDataRow (new object [] { 2, "mono test 3" }, LoadOption.Upsert);
+ Assert.AreEqual (DataRowState.Modified, dt.Rows [1].RowState, "#c7 should not change state");
+
+ // current - added; result - added
+ dt.Rows.Add (new object [] { 4, "mono 4" });
+ dt.LoadDataRow (new object [] { 4, "mono 4" }, LoadOption.Upsert);
+ Assert.AreEqual ("mono 4", dt.Rows [3] [1], "#c8 should change the current");
+ try {
+ object o = dt.Rows [3] [1, DataRowVersion.Original];
+ Assert.Fail ("#c9 should have thrown version not found exception");
+ } catch (VersionNotFoundException) { }
+ Assert.AreEqual (DataRowState.Added, dt.Rows [3].RowState, "#c10 should not change state");
+
+ // current - new; result - added
+ dt.LoadDataRow (new object [] { 5, "mono 5" }, LoadOption.Upsert);
+ Assert.AreEqual ("mono 5", dt.Rows [4] [1], "#c11 should change the current");
+ try {
+ object o = dt.Rows [4] [1, DataRowVersion.Original];
+ Assert.Fail ("#c12 should have thrown version not found exception");
+ } catch (VersionNotFoundException) { }
+ Assert.AreEqual (DataRowState.Added, dt.Rows [4].RowState, "#c13 should change state");
+
+ // current - deleted; result - added a new row
+ dt.AcceptChanges ();
+ dt.Rows [4].Delete ();
+ dt.LoadDataRow (new object [] { 5, "mono 5" }, LoadOption.Upsert);
+ Assert.AreEqual (6, dt.Rows.Count, "#c14 should not add a row");
+ Assert.AreEqual ("mono 5", dt.Rows [5] [1], "#c15 should change the current");
+ try {
+ object o = dt.Rows [5] [1, DataRowVersion.Original];
+ Assert.Fail ("#c16 expected version not found exception ");
+ } catch (VersionNotFoundException) {}
+ Assert.AreEqual (DataRowState.Added, dt.Rows [5].RowState, "#c17 should change state");
+ }
+
+ [Test]
+ public void LoadRowTestOverwriteChanges ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Rows.Add (new object [] { 1, "mono 1" });
+ dt.Rows.Add (new object [] { 2, "mono 2" });
+ dt.Rows.Add (new object [] { 3, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+ dt.AcceptChanges ();
+
+ dt.Rows [1] [1] = "overwrite";
+ Assert.AreEqual (DataRowState.Modified, dt.Rows [1].RowState, "#1 has not changed the row state");
+
+ dt.LoadDataRow (new object [] { 2, "mono test" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual (3, dt.Rows.Count, "#2 has not added a new row");
+ Assert.AreEqual ("mono test", dt.Rows [1] [1], "#3 should change the current");
+ Assert.AreEqual ("mono test", dt.Rows [1] [1, DataRowVersion.Original], "#4 should change the original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [1].RowState, "#5 has not changed the row state");
+
+ DataRow r = dt.Rows [1];
+ r [1] = "test";
+ Assert.AreEqual ("test", dt.Rows [1] [1], "#6 should change the current");
+ Assert.AreEqual ("mono test", dt.Rows [1] [1, DataRowVersion.Original], "#7 should change the original");
+ //Assert.AreEqual ("ramesh", dt.Rows [1] [1, DataRowVersion.Proposed], "#8 should change the original");
+
+ // Row State tests
+ // current - modified ; result - modified
+ dt.LoadDataRow (new object [] { 2, "mono test 2" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1], "#c1 should change the current");
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1, DataRowVersion.Original], "#c2 should change original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [1].RowState, "#c3 should not change state");
+
+ // current - Unchanged; result - Unchanged if no new value
+ dt.AcceptChanges ();
+ dt.LoadDataRow (new object [] { 2, "mono test 2" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1], "#c4 should change the current");
+ Assert.AreEqual ("mono test 2", dt.Rows [1] [1, DataRowVersion.Original], "#c5 should change original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [1].RowState, "#c6 should not change state");
+
+ // current - added; result - added
+ dt.Rows.Add (new object [] { 4, "mono 4" });
+ dt.LoadDataRow (new object [] { 4, "mono 4" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual ("mono 4", dt.Rows [3] [1], "#c8 should change the current");
+ Assert.AreEqual ("mono 4", dt.Rows [3] [1, DataRowVersion.Original], "#c9 should change the original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [3].RowState, "#c10 should not change state");
+
+ // current - new; result - added
+ dt.LoadDataRow (new object [] { 5, "mono 5" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual ("mono 5", dt.Rows [4] [1], "#c11 should change the current");
+ Assert.AreEqual ("mono 5", dt.Rows [4] [1, DataRowVersion.Original], "#c12 should change original");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [4].RowState, "#c13 should change state");
+
+ // current - deleted; result - added a new row
+ dt.AcceptChanges ();
+ dt.Rows [4].Delete ();
+ dt.LoadDataRow (new object [] { 5, "mono 51" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual (5, dt.Rows.Count, "#c14 should not add a row");
+ Assert.AreEqual ("mono 51", dt.Rows [4] [1], "#c15 should change the current");
+ Assert.AreEqual ("mono 51", dt.Rows [4] [1, DataRowVersion.Original], "#c16 should change the current");
+ Assert.AreEqual (DataRowState.Unchanged, dt.Rows [4].RowState, "#c17 should change state");
+ }
+
+ [Test]
+ public void LoadRowTestPreserveChanges ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Rows.Add (new object [] { 1, "mono 1" });
+ dt.Rows.Add (new object [] { 2, "mono 2" });
+ dt.Rows.Add (new object [] { 3, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+
+ dt.LoadDataRow (new object [] { 2, "mono test" }, LoadOption.PreserveChanges);
+ Assert.AreEqual (3, dt.Rows.Count, "#1 should not add a new row");
+ Assert.AreEqual ("mono 2", dt.Rows [1] [1], "#2 should not change the current");
+ Assert.AreEqual ("mono test", dt.Rows [1] [1, DataRowVersion.Original], "#3 should change the original");
+
+ dt.LoadDataRow (new object [] { 4, "mono 4" }, LoadOption.PreserveChanges);
+ Assert.AreEqual (4, dt.Rows.Count, "#5 should add a new row");
+ Assert.AreEqual ("mono 4", dt.Rows [3] [1], "#6 should change the current");
+ Assert.AreEqual ("mono 4", dt.Rows [3] [1, DataRowVersion.Original], "#7 should change the original");
+ }
+
+ [Test]
+ public void LoadRowDefaultValueTest ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("age", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Columns [1].DefaultValue = 20;
+
+ dt.Rows.Add (new object [] {1, 15, "mono 1"});
+ dt.Rows.Add (new object [] { 2, 25, "mono 2" });
+ dt.Rows.Add (new object [] { 3, 35, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+
+ dt.AcceptChanges ();
+
+ dt.LoadDataRow (new object [] { 2, null, "mono test" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual ( 3, dt.Rows.Count, "#1 should not have added a row");
+ Assert.AreEqual (20, dt.Rows [1] [1], "#2 should be default value");
+ Assert.AreEqual (20, dt.Rows [1] [1, DataRowVersion.Original], "#3 should be default value");
+
+ }
+
+ [Test]
+ public void LoadRowAutoIncrementTest ()
+ {
+ DataTable dt = new DataTable ();
+ dt.Columns.Add ("id", typeof (int));
+ dt.Columns.Add ("age", typeof (int));
+ dt.Columns.Add ("name", typeof (string));
+
+ dt.Columns [0].AutoIncrementSeed = 10;
+ dt.Columns [0].AutoIncrementStep = 5;
+ dt.Columns [0].AutoIncrement = true;
+
+ dt.Rows.Add (new object [] { null, 15, "mono 1" });
+ dt.Rows.Add (new object [] { null, 25, "mono 2" });
+ dt.Rows.Add (new object [] { null, 35, "mono 3" });
+
+ dt.PrimaryKey = new DataColumn [] { dt.Columns ["id"] };
+
+ dt.AcceptChanges ();
+
+ dt.LoadDataRow (new object [] { null, 20, "mono test" }, LoadOption.OverwriteChanges);
+ Assert.AreEqual (4, dt.Rows.Count, "#1 has not added a new row");
+ Assert.AreEqual (25, dt.Rows [3] [0], "#2 current should be ai");
+ Assert.AreEqual (25, dt.Rows [3] [0, DataRowVersion.Original], "#3 original should be ai");
+
+ }
+ }
+}
+
+#endif // NET_2_0