[Mono-list] System.Data.Common.DbDataAdapter buglet...

Mark Easton measton@tebiki.co.uk
Tue, 26 Aug 2003 08:19:12 +0100


This is a multi-part message in MIME format.

------=_NextPart_000_0010_01C36BAA.BB480EE0
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit

Having just found a ridiculously simple buglet in the DbDataAdapter.cs
file's 'BuildSchema' method, I thought I'd send some details through for
perusal. I would have included a test but haven't had a chance yet to
work out how the common data methods should be tested in a provider
agnostic fashion.

A description of the buglet follows:

The last call to 'ToArray' in 'BuildSchema' doesn't specify a type and
can be fixed by adding 'typeof(DataColumn)' as a parameter, as follows: 

Current Implementation:

if (MissingSchemaAction == MissingSchemaAction.AddWithKey &&
primaryKey.Count > 0){
				table.PrimaryKey =
(DataColumn[])primaryKey.ToArray();
			}

Fixed Implementation:

if (MissingSchemaAction == MissingSchemaAction.AddWithKey &&
primaryKey.Count > 0){
				table.PrimaryKey =
(DataColumn[])primaryKey.ToArray(typeof(DataColumn));
			}

Hope this is of help

Mark
______________________________________

mark.easton@blinksoftware.co.uk
+44(0)20 8406 3641
+44(0)77 1167 5960


------=_NextPart_000_0010_01C36BAA.BB480EE0
Content-Type: text/plain;
	name="DbDataAdapter.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="DbDataAdapter.cs"

//
// System.Data.Common.DbDataAdapter.cs
//
// Author:
//   Rodrigo Moya (rodrigo@ximian.com)
//   Tim Coleman (tim@timcoleman.com)
//
// (C) Ximian, Inc
// Copyright (C) 2002 Tim Coleman
//

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;

namespace System.Data.Common {
	public abstract class DbDataAdapter : DataAdapter, ICloneable
	{
		#region Fields

		public const string DefaultSourceTableName =3D "Table";
		const string DefaultSourceColumnName =3D "Column";

		#endregion // Fields
	=09
		#region Constructors

		protected DbDataAdapter()=20
		{
		}

		#endregion // Fields

		#region Properties

		IDbCommand DeleteCommand {
			get { return ((IDbDataAdapter) this).DeleteCommand; }
		}

		IDbCommand InsertCommand {
			get { return ((IDbDataAdapter) this).InsertCommand; }
		}

		IDbCommand SelectCommand {
			get { return ((IDbDataAdapter) this).SelectCommand; }
		}


		IDbCommand UpdateCommand {
			get { return ((IDbDataAdapter) this).UpdateCommand; }
		}

	 	#endregion // Properties
	=09
		#region Events

		[DataCategory ("Fill")]
		[DataSysDescription ("Event triggered when a recoverable error occurs =
during Fill.")]
		public event FillErrorEventHandler FillError;

		#endregion // Events

		#region Methods

		protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow =
dataRow, IDbCommand command, StatementType statementType, =
DataTableMapping tableMapping);
		protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent =
(DataRow dataRow, IDbCommand command, StatementType statementType, =
DataTableMapping tableMapping);

		private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, =
object[] values, Exception e)
		{
			FillErrorEventArgs args =3D new FillErrorEventArgs (dataTable, =
values);
			args.Errors =3D e;
			args.Continue =3D false;
			return args;
		}

		protected override void Dispose (bool disposing)
		{
			if (disposing) {
				((IDbDataAdapter) this).SelectCommand =3D null;
				((IDbDataAdapter) this).InsertCommand =3D null;
				((IDbDataAdapter) this).UpdateCommand =3D null;
				((IDbDataAdapter) this).DeleteCommand =3D null;
			}
		}

                public override int Fill (DataSet dataSet)
                {
			return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, =
CommandBehavior.Default);
                }

		public int Fill (DataTable dataTable)=20
		{
			if (dataTable =3D=3D null)
				throw new NullReferenceException ();

			return Fill (dataTable, SelectCommand, CommandBehavior.Default);
		}

		public int Fill (DataSet dataSet, string srcTable)=20
		{
			return Fill (dataSet, 0, 0, srcTable, SelectCommand, =
CommandBehavior.Default);
		}

		protected virtual int Fill (DataTable dataTable, IDataReader =
dataReader)=20
		{
			int count =3D 0;
			bool doContinue =3D true;

			if (dataReader.FieldCount =3D=3D 0) {
				dataReader.Close ();
				return 0;
			}

			object[] itemArray =3D new object [dataReader.FieldCount];
			SetupSchema (SchemaType.Mapped, dataTable.TableName, dataTable); // =
FIXME
			BuildSchema (dataReader, dataTable, SchemaType.Mapped);

			while (doContinue && dataReader.Read ()) {
				dataReader.GetValues (itemArray);
				try {
					dataTable.BeginLoadData ();
					dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
					dataTable.EndLoadData ();
					count +=3D 1;
				}
				catch (Exception e) {
					FillErrorEventArgs args =3D CreateFillErrorEvent (dataTable, =
itemArray, e);
					OnFillError (args);
					doContinue =3D args.Continue;
				}
			}
			dataReader.Close ();

			return count;
		}

		protected virtual int Fill (DataTable dataTable, IDbCommand command, =
CommandBehavior behavior)=20
		{
			return Fill (dataTable, command.ExecuteReader (behavior));
		}

		public int Fill (DataSet dataSet, int startRecord, int maxRecords, =
string srcTable)=20
		{
			return this.Fill (dataSet, startRecord, maxRecords, srcTable, =
SelectCommand, CommandBehavior.Default);
		}

		protected virtual int Fill (DataSet dataSet, string srcTable, =
IDataReader dataReader, int startRecord, int maxRecords)=20
		{
			if (startRecord < 0)
				throw new ArgumentException ("The startRecord parameter was less =
than 0.");
			if (maxRecords < 0)
				throw new ArgumentException ("The maxRecords parameter was less than =
0.");

			if (dataReader.FieldCount =3D=3D 0) {
				dataReader.Close ();
				return 0;
			}

                        DataTable dataTable;
                        int resultIndex =3D 0;
                        int count =3D 0;
			bool doContinue =3D true;

			string tableName =3D srcTable;
			object[] itemArray =3D new object [dataReader.FieldCount];

                       	do {
				dataTable =3D new DataTable ();
				SetupSchema (SchemaType.Mapped, tableName, dataTable);

				if (dataSet.Tables.Contains (dataTable.TableName))
					dataTable =3D dataSet.Tables [tableName];
				BuildSchema (dataReader, dataTable, SchemaType.Mapped);

				for (int i =3D 0; i < startRecord; i +=3D 1)
					dataReader.Read ();

				while (doContinue && dataReader.Read () && !(maxRecords > 0 && count =
>=3D maxRecords)) {
					dataReader.GetValues (itemArray);
					try {
						dataTable.BeginLoadData ();
						dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
						dataTable.EndLoadData ();
						count +=3D 1;
					}
					catch (Exception e) {
						FillErrorEventArgs args =3D CreateFillErrorEvent (dataTable, =
itemArray, e);
						OnFillError (args);
						doContinue =3D args.Continue;
					}
				}

				dataSet.Tables.Add (dataTable);
                               	tableName =3D String.Format ("{0}{1}", =
srcTable, ++resultIndex);

				startRecord =3D 0;
				maxRecords =3D 0;

                       	} while (doContinue && dataReader.NextResult =
());

                        dataReader.Close ();

                        return count;
		}


		protected virtual int Fill (DataSet dataSet, int startRecord, int =
maxRecords, string srcTable, IDbCommand command, CommandBehavior =
behavior)=20
		{
			CommandBehavior commandBehavior =3D behavior;
			if (command.Connection.State =3D=3D ConnectionState.Closed) {
				command.Connection.Open ();
				commandBehavior |=3D CommandBehavior.CloseConnection;
			}
			return Fill (dataSet, srcTable, command.ExecuteReader =
(commandBehavior), startRecord, maxRecords);
		}

		public override DataTable[] FillSchema (DataSet dataSet, SchemaType =
schemaType)=20
		{
			return FillSchema (dataSet, schemaType, SelectCommand, =
DefaultSourceTableName, CommandBehavior.Default);
		}

		public DataTable FillSchema (DataTable dataTable, SchemaType =
schemaType)=20
		{
			return FillSchema (dataTable, schemaType, SelectCommand, =
CommandBehavior.Default);
		}

		public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, =
string srcTable)=20
		{
			return FillSchema (dataSet, schemaType, SelectCommand, srcTable, =
CommandBehavior.Default);
		}

		[MonoTODO ("Verify")]
		protected virtual DataTable FillSchema (DataTable dataTable, =
SchemaType schemaType, IDbCommand command, CommandBehavior behavior)=20
		{
			DataTable table;
			behavior |=3D CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
			if (command.Connection.State =3D=3D ConnectionState.Closed) {
				command.Connection.Open ();
				behavior |=3D CommandBehavior.CloseConnection;
			}

			IDataReader reader =3D command.ExecuteReader (behavior);
			table =3D new DataTable ();
			SetupSchema (schemaType, DefaultSourceTableName, table);
			BuildSchema (reader, table, schemaType);

			reader.Close ();
			return table;
		}

		[MonoTODO ("Verify")]
		protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType =
schemaType, IDbCommand command, string srcTable, CommandBehavior =
behavior)=20
		{
			behavior |=3D CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
			if (command.Connection.State =3D=3D ConnectionState.Closed) {
				command.Connection.Open ();
				behavior |=3D CommandBehavior.CloseConnection;
			}

			IDataReader reader =3D command.ExecuteReader (behavior);
			ArrayList output =3D new ArrayList ();
			string tableName =3D srcTable;
			int index =3D 0;

			do {
				DataTable table =3D new DataTable ();
				SetupSchema (schemaType, tableName, table);
				if (dataSet.Tables.Contains (table.TableName))
					table =3D dataSet.Tables [table.TableName];=09
				else
					dataSet.Tables.Add (table);
				BuildSchema (reader, table, schemaType);
				output.Add (table);
				tableName =3D String.Format ("{0}{1}", srcTable, ++index);
			} while (reader.NextResult ());
			reader.Close ();
			return (DataTable[]) output.ToArray (typeof (DataTable));
		}

		private void SetupSchema (SchemaType schemaType, string =
sourceTableName, DataTable table)
		{
			DataTableMapping tableMapping =3D null;

			if (schemaType =3D=3D SchemaType.Mapped)=20
				tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
sourceTableName, "", MissingMappingAction.Ignore);

			if (tableMapping !=3D null)
				table.TableName =3D tableMapping.DataSetTable;
			else
				table.TableName =3D sourceTableName;
		}

		[EditorBrowsable (EditorBrowsableState.Advanced)]
		public override IDataParameter[] GetFillParameters ()=20
		{
			object[] parameters =3D new object [SelectCommand.Parameters.Count];
			SelectCommand.Parameters.CopyTo (parameters, 0);
			return (IDataParameter[]) parameters;
		}

		[MonoTODO ("Test")]
		private void BuildSchema (IDataReader reader, DataTable table, =
SchemaType schemaType)
		{
			ArrayList primaryKey =3D new ArrayList ();
			ArrayList sourceColumns =3D new ArrayList ();

			foreach (DataRow schemaRow in reader.GetSchemaTable ().Rows) {
				// generate a unique column name in the source table.
				string sourceColumnName;
				if (schemaRow ["ColumnName"].Equals (DBNull.Value))
					sourceColumnName =3D DefaultSourceColumnName;
				else=20
					sourceColumnName =3D (string) schemaRow ["ColumnName"];

				string realSourceColumnName =3D sourceColumnName;

				for (int i =3D 1; sourceColumns.Contains (realSourceColumnName); i =
+=3D 1)=20
					realSourceColumnName =3D String.Format ("{0}{1}", sourceColumnName, =
i);
				sourceColumns.Add(realSourceColumnName);

				// generate DataSetColumnName from DataTableMapping, if any
				string dsColumnName =3D realSourceColumnName;
				DataTableMapping tableMapping =3D null;
				if (schemaType =3D=3D SchemaType.Mapped)
					tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
table.TableName, table.TableName, MissingMappingAction.Ignore);=20
				if (tableMapping !=3D null) {
					table.TableName =3D tableMapping.DataSetTable;

					// check to see if the column mapping exists
					if (tableMapping.ColumnMappings.Contains (dsColumnName)) {
						dsColumnName =3D tableMapping.ColumnMappings =
[realSourceColumnName].DataSetColumn;
					} else {
						if (MissingSchemaAction =3D=3D MissingSchemaAction.Error)
							throw new SystemException ();
						tableMapping.ColumnMappings.Add (sourceColumnName, dsColumnName);
					}
					if (!TableMappings.Contains (tableMapping))
						TableMappings.Add (tableMapping);
				}

				if (!table.Columns.Contains(dsColumnName))
					table.Columns.Add (dsColumnName, (Type) schemaRow ["DataType"]);

				if (!schemaRow["IsKey"].Equals (DBNull.Value))
					if ((bool) (schemaRow ["IsKey"]))
						primaryKey.Add (table.Columns [dsColumnName]);=09
			}
			if (MissingSchemaAction =3D=3D MissingSchemaAction.AddWithKey && =
primaryKey.Count > 0){
				table.PrimaryKey =3D =
(DataColumn[])primaryKey.ToArray(typeof(DataColumn));
			}
		}

		[MonoTODO]
		object ICloneable.Clone ()
		{
			throw new NotImplementedException ();
		}

		[MonoTODO]
		public int Update (DataRow[] dataRows)=20
		{
			throw new NotImplementedException (); // FIXME: Which mapping?
		}

		public override int Update (DataSet dataSet)=20
		{
			return Update (dataSet, DefaultSourceTableName);
		}

		public int Update (DataTable dataTable)=20
		{
			int index =3D TableMappings.IndexOfDataSetTable =
(dataTable.TableName);
			if (index < 0)
				throw new ArgumentException ();
			return Update (dataTable, TableMappings [index]);
		}

		private int Update (DataTable dataTable, DataTableMapping =
tableMapping)
		{
			DataRow[] rows =3D new DataRow [dataTable.Rows.Count];
			dataTable.Rows.CopyTo (rows, 0);
			return Update (rows, tableMapping);
		}

		[MonoTODO]
		protected virtual int Update (DataRow[] dataRows, DataTableMapping =
tableMapping)=20
		{
			int updateCount =3D 0;
			foreach (DataRow row in dataRows) {
				StatementType statementType =3D StatementType.Update;
				IDbCommand command =3D null;
				string commandName =3D String.Empty;
				bool useCommandBuilder =3D false;

				switch (row.RowState) {
				case DataRowState.Added:
					statementType =3D StatementType.Insert;
					command =3D InsertCommand;
					commandName =3D "Insert";
					break;
				case DataRowState.Deleted:
					statementType =3D StatementType.Delete;
					command =3D DeleteCommand;
					commandName =3D "Delete";
					break;
				case DataRowState.Modified:
					statementType =3D StatementType.Update;
					command =3D UpdateCommand;
					commandName =3D "Update";
					break;
				case DataRowState.Unchanged:
					continue;
				case DataRowState.Detached:
					throw new NotImplementedException ();
				}

				if (command =3D=3D null)
					useCommandBuilder =3D true;

				RowUpdatingEventArgs args =3D CreateRowUpdatingEvent (row, command, =
statementType, tableMapping);
				OnRowUpdating (args);

				if (args.Status =3D=3D UpdateStatus.ErrorsOccurred)
					throw (args.Errors);

				if (command =3D=3D null && args.Command !=3D null)
					command =3D args.Command;
				else if (command =3D=3D null)
					throw new InvalidOperationException (String.Format ("Update =
requires a valid {0}Command when passed a DataRow collection with =
modified rows.", commandName));

				if (!useCommandBuilder) {
					DataColumnMappingCollection columnMappings =3D =
tableMapping.ColumnMappings;

					foreach (IDataParameter parameter in command.Parameters) {
						string dsColumnName =3D parameter.SourceColumn;
						DataColumnMapping mapping =3D columnMappings =
[parameter.SourceColumn];
						if (mapping !=3D null) dsColumnName =3D mapping.DataSetColumn;
						DataRowVersion rowVersion =3D DataRowVersion.Default;

						// Parameter version is ignored for non-update commands
						if (statementType =3D=3D StatementType.Update)=20
							rowVersion =3D parameter.SourceVersion;
						if (statementType =3D=3D StatementType.Delete)=20
							rowVersion =3D DataRowVersion.Original;

						parameter.Value =3D row [dsColumnName, rowVersion];
					}
					row.AcceptChanges ();
				}
				updateCount +=3D command.ExecuteNonQuery ();

				OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, =
tableMapping));
			}
			return updateCount;
		}

		public int Update (DataSet dataSet, string sourceTable)=20
		{
			MissingMappingAction mappingAction =3D MissingMappingAction;
			if (mappingAction =3D=3D MissingMappingAction.Ignore)
				mappingAction =3D MissingMappingAction.Error;
			DataTableMapping tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
sourceTable, sourceTable, mappingAction);

			DataTable dataTable =3D dataSet.Tables[tableMapping.DataSetTable];
			if (dataTable =3D=3D null)
			    throw new ArgumentException ("sourceTable");

			return Update (dataTable, tableMapping);
		}

		protected virtual void OnFillError (FillErrorEventArgs value)=20
		{
			if (FillError !=3D null)
				FillError (this, value);
		}

		protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
		protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
	=09
		#endregion // Methods
	}
}

------=_NextPart_000_0010_01C36BAA.BB480EE0--