2005-11-21 Senganal T <tsenganal@novell.com>
[mono.git] / mcs / class / System.Data / System.Data.Common / DbDataAdapter.cs
1 //
2 // System.Data.Common.DbDataAdapter.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Tim Coleman (tim@timcoleman.com)
7 //   Sureshkumar T <tsureshkumar@novell.com>
8 //
9 // (C) Ximian, Inc
10 // Copyright (C) Tim Coleman, 2002-2003
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Collections;
38 using System.ComponentModel;
39 using System.Data;
40 using System.Runtime.InteropServices;
41
42 namespace System.Data.Common {
43 #if NET_2_0
44         public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
45 #else
46         public abstract class DbDataAdapter : DataAdapter, ICloneable
47 #endif
48         {
49                 #region Fields
50
51                 public const string DefaultSourceTableName = "Table";
52                 const string DefaultSourceColumnName = "Column";
53
54                 #endregion // Fields
55                 
56                 #region Constructors
57
58                 protected DbDataAdapter() 
59                 {
60                 }
61
62                 [MonoTODO]
63                 protected DbDataAdapter(DbDataAdapter adapter) : base(adapter)
64                 {
65                 }
66
67                 #endregion // Fields
68
69                 #region Properties
70
71 #if NET_2_0
72                 [MonoTODO]
73                 protected virtual IDbConnection BaseConnection {
74                         get { throw new NotImplementedException (); }
75                         set { throw new NotImplementedException (); }
76                 }
77
78                 public IDbConnection Connection { 
79                         get { return BaseConnection; }
80                         set { BaseConnection = value; }
81                 }
82 #endif
83
84
85 #if NET_2_0
86                 protected internal CommandBehavior FillCommandBehavior {
87                         get { throw new NotImplementedException (); }
88                         set { throw new NotImplementedException (); }
89                 }
90 #endif
91
92
93 #if NET_2_0
94                 [MonoTODO]
95                 protected virtual IDbCommand this [[Optional] StatementType statementType] {
96                         get { throw new NotImplementedException (); }
97                         set { throw new NotImplementedException (); }
98                 }
99
100                 [MonoTODO]
101                 protected virtual DbProviderFactory ProviderFactory {
102                         get { throw new NotImplementedException (); }
103                 }
104
105                 [MonoTODO]
106                 IDbCommand IDbDataAdapter.SelectCommand {
107                         get { throw new NotImplementedException(); }
108                         set { throw new NotImplementedException(); }
109                 }
110
111                 [MonoTODO]
112                 IDbCommand IDbDataAdapter.UpdateCommand{
113                         get { throw new NotImplementedException(); }
114                         set { throw new NotImplementedException(); }
115                 }
116
117                 [MonoTODO]
118                 IDbCommand IDbDataAdapter.DeleteCommand{
119                         get { throw new NotImplementedException(); }
120                         set { throw new NotImplementedException(); }
121                 }
122
123                 [MonoTODO]
124                 IDbCommand IDbDataAdapter.InsertCommand{
125                         get { throw new NotImplementedException(); }
126                         set { throw new NotImplementedException(); }
127                 }
128                 
129                 [MonoTODO]
130                 public DbCommand SelectCommand {
131                         get { throw new NotImplementedException(); }
132                         set { throw new NotImplementedException(); }
133                 }
134
135                 [MonoTODO]
136                 public DbCommand DeleteCommand {
137                         get { throw new NotImplementedException(); }
138                         set { throw new NotImplementedException(); }
139                 }
140
141                 [MonoTODO]
142                 public DbCommand InsertCommand {
143                         get { throw new NotImplementedException(); }
144                         set { throw new NotImplementedException(); }
145                 }
146
147                 [MonoTODO]
148                 public DbCommand UpdateCommand {
149                         get { throw new NotImplementedException(); }
150                         set { throw new NotImplementedException(); }
151                 }
152
153                 [MonoTODO]
154                 public IDbTransaction Transaction {
155                         get { throw new NotImplementedException (); }
156                         set { throw new NotImplementedException (); }
157                 }
158
159                 [MonoTODO]
160                 public int UpdateBatchSize {
161                         get { throw new NotImplementedException (); }
162                         set { throw new NotImplementedException (); }
163                 }
164 #else
165                 IDbCommand SelectCommand {
166                         get { return ((IDbDataAdapter) this).SelectCommand; }
167                 }
168
169                 IDbCommand UpdateCommand {
170                         get { return ((IDbDataAdapter) this).UpdateCommand; }
171                 }
172
173                 IDbCommand DeleteCommand {
174                         get { return ((IDbDataAdapter) this).DeleteCommand; }
175                 }
176
177                 IDbCommand InsertCommand {
178                         get { return ((IDbDataAdapter) this).InsertCommand; }
179                 } 
180 #endif
181
182                 #endregion // Properties
183                 
184                 #region Events
185
186 #if ONLY_1_0 || ONLY_1_1
187
188                 [DataCategory ("Fill")]
189                 [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
190                 public event FillErrorEventHandler FillError;
191
192 #endif
193                 #endregion // Events
194
195                 #region Methods
196
197 #if NET_2_0
198                 [MonoTODO]
199                 public virtual void BeginInit ()
200                 {
201                         throw new NotImplementedException ();
202                 }
203 #endif
204
205                 protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
206                 protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
207
208                 private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, object[] values, Exception e)
209                 {
210                         FillErrorEventArgs args = new FillErrorEventArgs (dataTable, values);
211                         args.Errors = e;
212                         args.Continue = false;
213                         return args;
214                 }
215
216                 protected override void Dispose (bool disposing)
217                 {
218                         if (disposing) {
219                                 IDbDataAdapter da = (IDbDataAdapter) this;
220                                 if (da.SelectCommand != null) {
221                                         da.SelectCommand.Dispose();
222                                         da.SelectCommand = null;
223                                 }
224                                 if (da.InsertCommand != null) {
225                                         da.InsertCommand.Dispose();
226                                         da.InsertCommand = null;
227                                 }
228                                 if (da.UpdateCommand != null) {
229                                         da.UpdateCommand.Dispose();
230                                         da.UpdateCommand = null;
231                                 }
232                                 if (da.DeleteCommand != null) {
233                                         da.DeleteCommand.Dispose();
234                                         da.DeleteCommand = null;
235                                 }
236                         }
237                 }
238
239 #if NET_2_0
240                 [MonoTODO]
241                 public virtual void EndInit ()
242                 {
243                         throw new NotImplementedException ();
244                 }
245 #endif
246
247                 public override int Fill (DataSet dataSet)
248                 {
249                         return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, CommandBehavior.Default);
250                 }
251
252                 public int Fill (DataTable dataTable) 
253                 {
254                         if (dataTable == null)
255                                 throw new NullReferenceException ();
256
257                         return Fill (dataTable, SelectCommand, CommandBehavior.Default);
258                 }
259
260                 public int Fill (DataSet dataSet, string srcTable) 
261                 {
262                         return Fill (dataSet, 0, 0, srcTable, SelectCommand, CommandBehavior.Default);
263                 }
264
265 #if NET_2_0
266                 protected override int Fill (DataTable dataTable, IDataReader dataReader) 
267 #else
268                         protected virtual int Fill (DataTable dataTable, IDataReader dataReader) 
269 #endif
270                 {
271                         if (dataReader.FieldCount == 0) {
272                                 dataReader.Close ();
273                                 return 0;
274                         }
275                         
276                         int count = 0;
277
278                         try {
279                                 string tableName = SetupSchema (SchemaType.Mapped, dataTable.TableName);
280                                 if (tableName != null) {
281                                         dataTable.TableName = tableName;
282                                         FillTable (dataTable, dataReader, 0, 0, ref count);
283                                 }
284                         } finally {
285                                 dataReader.Close ();
286                         }
287
288                         return count;
289                 }
290
291                 protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior) 
292                 {
293                         CommandBehavior commandBehavior = behavior;
294                         // first see that the connection is not close.
295                         if (command.Connection.State == ConnectionState.Closed) 
296                         {
297                                 command.Connection.Open ();
298                                 commandBehavior |= CommandBehavior.CloseConnection;
299                         }
300                         return Fill (dataTable, command.ExecuteReader (commandBehavior));
301                 }
302
303 #if NET_2_0
304                 [MonoTODO]
305                 public int Fill (int startRecord, int maxRecords, DataTable[] dataTables)
306                 {
307                         throw new NotImplementedException ();
308                 }
309 #endif
310
311                 public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable) 
312                 {
313                         return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default);
314                 }
315
316 #if NET_2_0
317                 [MonoTODO]
318                 protected virtual int Fill (DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior)
319                 {
320                         throw new NotImplementedException ();
321                 }
322 #endif
323
324 #if NET_2_0
325                 protected override int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords) 
326 #else
327                         protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords) 
328 #endif
329                 {
330                         if (startRecord < 0)
331                                 throw new ArgumentException ("The startRecord parameter was less than 0.");
332                         if (maxRecords < 0)
333                                 throw new ArgumentException ("The maxRecords parameter was less than 0.");
334
335                         DataTable dataTable;
336                         int resultIndex = 0;
337                         int count = 0;
338                         
339                         try {
340                                 string tableName = srcTable;
341                                 do {
342                                         // Non-resultset queries like insert, delete or update aren't processed.
343                                         if (dataReader.FieldCount != -1)
344                                         {
345                                                 tableName = SetupSchema (SchemaType.Mapped, tableName);
346                                                 if (tableName != null) {
347                                                 
348                                                         // check if the table exists in the dataset
349                                                         if (dataSet.Tables.Contains (tableName)) 
350                                                                 // get the table from the dataset
351                                                                 dataTable = dataSet.Tables [tableName];
352                                                         else {
353                                                                 dataTable = new DataTable(tableName);
354                                                                 dataSet.Tables.Add (dataTable);
355                                                         }
356         
357                                                         if (!FillTable (dataTable, dataReader, startRecord, maxRecords, ref count)) {
358                                                                 continue;
359                                                         }
360         
361                                                         tableName = String.Format ("{0}{1}", srcTable, ++resultIndex);
362         
363                                                         startRecord = 0;
364                                                         maxRecords = 0;
365                                                 }
366                                         }
367                                 } while (dataReader.NextResult ());
368                         } 
369                         finally {
370                                 dataReader.Close ();
371                         }
372
373                         return count;
374                 }
375                 
376                 protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior) 
377                 {
378                         if (MissingSchemaAction == MissingSchemaAction.AddWithKey)
379                                 behavior |= CommandBehavior.KeyInfo;
380                         CommandBehavior commandBehavior = behavior;
381                         if (command.Connection.State == ConnectionState.Closed) {
382                                 command.Connection.Open ();
383                                 commandBehavior |= CommandBehavior.CloseConnection;
384                         }
385                         return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
386                 }
387
388                 private bool FillTable (DataTable dataTable, IDataReader dataReader, int startRecord, int maxRecords, ref int counter)
389                 {
390                         if (dataReader.FieldCount == 0)
391                                 return false;
392
393                         int counterStart = counter;
394
395                         int[] mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
396
397                         int[] sortedMapping = new int[mapping.Length];
398                         int length = sortedMapping.Length;
399                         for(int i=0; i < sortedMapping.Length; i++) {
400                                 if (mapping[i] >= 0)
401                                         sortedMapping[mapping[i]] = i;
402                                 else
403                                         sortedMapping[--length] = i;
404                         }
405
406                         for (int i = 0; i < startRecord; i++) {
407                                 dataReader.Read ();
408                         }
409
410                         while (dataReader.Read () && (maxRecords == 0 || (counter - counterStart) < maxRecords)) {
411                                 try {
412                                         dataTable.BeginLoadData ();
413                                         dataTable.LoadDataRow (dataReader, sortedMapping, length, AcceptChangesDuringFill);
414                                         dataTable.EndLoadData ();
415                                         counter++;
416                                 } 
417                                 catch (Exception e) {
418                                         object[] readerArray = new object[dataReader.FieldCount];
419                                         object[] tableArray = new object[mapping.Length];
420                                         // we get the values from the datareader
421                                         dataReader.GetValues (readerArray);
422                                         // copy from datareader columns to table columns according to given mapping
423                                         for (int i = 0; i < mapping.Length; i++) {
424                                                 if (mapping[i] >= 0) {
425                                                         tableArray[i] = readerArray[mapping[i]];
426                                                 }
427                                         }
428                                         FillErrorEventArgs args = CreateFillErrorEvent (dataTable, tableArray, e);
429                                         OnFillError (args);
430                                         if(!args.Continue) {
431                                                 return false;
432                                         }
433                                 }
434                         }
435                         return true;
436                 }
437
438 #if NET_2_0
439                 /// <summary>
440                 ///     Fills the given datatable using values from reader. if a value 
441                 ///     for a column is  null, that will be filled with default value. 
442                 /// </summary>
443                 /// <returns>No. of rows affected </returns>
444                 internal static int FillFromReader (DataTable table,
445                                                     IDataReader reader,
446                                                     int start, 
447                                                     int length,
448                                                     int [] mapping,
449                                                     LoadOption loadOption
450                                                     )
451                 {
452                         if (reader.FieldCount == 0)
453                                 return 0 ;
454
455                         for (int i = 0; i < start; i++)
456                                 reader.Read ();
457
458                         int counter = 0;
459                         object [] values = new object [mapping.Length];
460                         while (reader.Read () &&
461                                (length == 0 || counter < length)) {
462                                 
463                                 for (int i = 0 ; i < mapping.Length; i++)
464                                         values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
465                                         
466                                 table.BeginLoadData ();
467                                 table.LoadDataRow (values, loadOption);
468                                 table.EndLoadData ();
469                                 counter++;
470                         }
471                         return counter;
472                 }
473
474 #endif // NET_2_0
475
476                 public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType) 
477                 {
478                         return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
479                 }
480
481                 public DataTable FillSchema (DataTable dataTable, SchemaType schemaType) 
482                 {
483                         return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default);
484                 }
485
486                 public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable) 
487                 {
488                         return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default);
489                 }
490
491                 [MonoTODO ("Verify")]
492                 protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior) 
493                 {
494                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
495                         if (command.Connection.State == ConnectionState.Closed) {
496                                 command.Connection.Open ();
497                                 behavior |= CommandBehavior.CloseConnection;
498                         }
499
500                         IDataReader reader = command.ExecuteReader (behavior);
501                         try
502                         {
503                                 string tableName =  SetupSchema (schemaType, dataTable.TableName);
504                                 if (tableName != null)
505                                 {
506                                         BuildSchema (reader, dataTable, schemaType);
507                                 }
508                         }
509                         finally
510                         {
511                                 reader.Close ();
512                         }
513                         return dataTable;
514                 }
515
516                 [MonoTODO ("Verify")]
517                 protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) 
518                 {
519                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
520                         if (command.Connection.State == ConnectionState.Closed) {
521                                 command.Connection.Open ();
522                                 behavior |= CommandBehavior.CloseConnection;
523                         }
524
525                         IDataReader reader = command.ExecuteReader (behavior);
526                         ArrayList output = new ArrayList ();
527                         string tableName = srcTable;
528                         int index = 0;
529                         DataTable table;
530                         try
531                         {
532                                 tableName = SetupSchema (schemaType, tableName);
533                                 if (tableName != null)
534                                 {
535                                         if (dataSet.Tables.Contains (tableName))
536                                                 table = dataSet.Tables [tableName];     
537                                         else
538                                         {
539                                                 table = new DataTable(tableName);
540                                                 dataSet.Tables.Add (table);
541                                         }
542                                         BuildSchema (reader, table, schemaType);
543                                         output.Add (table);
544                                         tableName = String.Format ("{0}{1}", srcTable, ++index);
545                                 }
546                         }
547                         finally
548                         {
549                                 reader.Close ();
550                         }
551                         return (DataTable[]) output.ToArray (typeof (DataTable));
552                 }
553
554 #if NET_2_0
555                 [MonoTODO]
556                 public DataSet GetDataSet ()
557                 {
558                         throw new NotImplementedException ();
559                 }
560
561                 [MonoTODO]
562                 public DataTable GetDataTable ()
563                 {
564                         throw new NotImplementedException ();
565                 }
566 #endif
567
568                 private string SetupSchema (SchemaType schemaType, string sourceTableName)
569                 {
570                         DataTableMapping tableMapping = null;
571
572                         if (schemaType == SchemaType.Mapped) 
573                         {
574                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, sourceTableName, MissingMappingAction);
575
576                                 if (tableMapping != null)
577                                         return tableMapping.DataSetTable;
578                                 return null;
579                         }
580                         else
581                                 return sourceTableName;
582                 }
583
584                 [EditorBrowsable (EditorBrowsableState.Advanced)]
585                 public override IDataParameter[] GetFillParameters () 
586                 {
587                         IDataParameter[] parameters = new IDataParameter[SelectCommand.Parameters.Count];
588                         SelectCommand.Parameters.CopyTo (parameters, 0);
589                         return parameters;
590                 }
591                 
592                 // this method builds the schema for a given datatable. it returns a int array with 
593                 // "array[ordinal of datatable column] == index of source column in data reader".
594                 // each column in the datatable has a mapping to a specific column in the datareader,
595                 // the int array represents this match.
596                 [MonoTODO ("Test")]
597                 private int[] BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
598                 {
599                         return BuildSchema (reader, table, schemaType, MissingSchemaAction,
600                                             MissingMappingAction, TableMappings);
601                 }
602
603                 /// <summary>
604                 ///     Creates or Modifies the schema of the given DataTable based on the schema of
605                 ///     the reader and the arguments passed.
606                 /// </summary>
607                 internal static int[] BuildSchema (IDataReader reader,
608                                                    DataTable table,
609                                                    SchemaType schemaType,
610                                                    MissingSchemaAction missingSchAction,
611                                                    MissingMappingAction missingMapAction,
612                                                    DataTableMappingCollection dtMapping
613                                                    )
614                 {
615                         int readerIndex = 0;
616                         // FIXME : this fails if query has fewer columns than a table
617                         int[] mapping = new int[table.Columns.Count]; // mapping the reader indexes to the datatable indexes
618                         
619                         for(int i=0; i < mapping.Length; i++) {
620                                 mapping[i] = -1;
621                         }
622                         
623                         ArrayList primaryKey = new ArrayList ();
624                         ArrayList sourceColumns = new ArrayList ();
625                         bool createPrimaryKey = true;
626                         
627                         DataTable schemaTable = reader.GetSchemaTable ();
628                         int colIndex;
629                         DataColumn ColumnNameCol                = ((colIndex = schemaTable.Columns.IndexOf("ColumnName")) >= 0) ? schemaTable.Columns[colIndex] : null;
630                         DataColumn DataTypeCol                  = ((colIndex = schemaTable.Columns.IndexOf("DataType")) >= 0) ? schemaTable.Columns[colIndex] : null;
631                         DataColumn IsAutoIncrementCol   = ((colIndex = schemaTable.Columns.IndexOf("IsAutoIncrement")) >= 0) ? schemaTable.Columns[colIndex] : null;
632                         DataColumn AllowDBNullCol               = ((colIndex = schemaTable.Columns.IndexOf("AllowDBNull")) >= 0) ? schemaTable.Columns[colIndex] : null;
633                         DataColumn IsReadOnlyCol                = ((colIndex = schemaTable.Columns.IndexOf("IsReadOnly")) >= 0) ? schemaTable.Columns[colIndex] : null;
634                         DataColumn IsKeyCol                             = ((colIndex = schemaTable.Columns.IndexOf("IsKey")) >= 0) ? schemaTable.Columns[colIndex] : null;
635                         DataColumn IsUniqueCol                  = ((colIndex = schemaTable.Columns.IndexOf("IsUnique")) >= 0) ? schemaTable.Columns[colIndex] : null;
636                         DataColumn ColumnSizeCol                = ((colIndex = schemaTable.Columns.IndexOf("ColumnSize")) >= 0) ? schemaTable.Columns[colIndex] : null;
637
638                         foreach (DataRow schemaRow in schemaTable.Rows) {
639                                 // generate a unique column name in the source table.
640                                 string sourceColumnName;
641                                 if (ColumnNameCol == null || schemaRow.IsNull(ColumnNameCol))
642                                         sourceColumnName = DefaultSourceColumnName;
643                                 else 
644                                         sourceColumnName = (string) schemaRow [ColumnNameCol];
645
646                                 string realSourceColumnName = sourceColumnName;
647
648                                 for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1) 
649                                         realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i);
650                                 sourceColumns.Add(realSourceColumnName);
651
652                                 // generate DataSetColumnName from DataTableMapping, if any
653                                 string dsColumnName = realSourceColumnName;
654                                 DataTableMapping tableMapping = null;
655                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (dtMapping, table.TableName, table.TableName, missingMapAction); 
656                                 if (tableMapping != null) 
657                                 {
658                                         
659                                         table.TableName = tableMapping.DataSetTable;
660                                         // check to see if the column mapping exists
661                                         DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, missingMapAction);
662                                         if (columnMapping != null)
663                                         {
664                                                 Type columnType = (Type)schemaRow[DataTypeCol];
665                                                 DataColumn col =
666                                                         columnMapping.GetDataColumnBySchemaAction(
667                                                                                                   table ,
668                                                                                                   columnType,
669                                                                                                   missingSchAction);
670
671                                                 if (col != null)
672                                                 {
673                                                         // if the column is not in the table - add it.
674                                                         if (table.Columns.IndexOf(col) == -1)
675                                                         {
676                                                                 if (missingSchAction == MissingSchemaAction.Add 
677                                                                     || missingSchAction == MissingSchemaAction.AddWithKey)
678                                                                         table.Columns.Add(col);
679
680                                                                 int[] tmp = new int[mapping.Length + 1];
681                                                                 Array.Copy(mapping,0,tmp,0,col.Ordinal);
682                                                                 Array.Copy(mapping,col.Ordinal,tmp,col.Ordinal + 1,mapping.Length - col.Ordinal);
683                                                                 mapping = tmp;
684                                                         }                               
685
686                                                         object value = (AllowDBNullCol != null) ? schemaRow[AllowDBNullCol] : null;
687                                                         bool allowDBNull = value is bool ? (bool)value : true;
688                                                         col.AllowDBNull = allowDBNull; 
689                                                         value = (IsKeyCol != null) ? schemaRow[IsKeyCol] : null;
690                                                         bool isKey = value is bool ? (bool)value : false;
691
692                                                         if (missingSchAction == MissingSchemaAction.AddWithKey) {
693                                     
694                                                                 value = (IsAutoIncrementCol != null) ? schemaRow[IsAutoIncrementCol] : null;
695                                                                 bool isAutoIncrement = value is bool ? (bool)value : false;
696                                                                 value = (IsReadOnlyCol != null) ? schemaRow[IsReadOnlyCol] : null;
697                                                                 bool isReadOnly = value is bool ? (bool)value : false;
698                                                                 value = (IsUniqueCol != null) ? schemaRow[IsUniqueCol] : null;
699                                                                 bool isUnique = value is bool ? (bool)value : false;
700                                                                 
701                                                                 // fill woth key info                                                           
702                                                                 if (isAutoIncrement && DataColumn.CanAutoIncrement(columnType)) {
703                                                                         col.AutoIncrement = true;
704                                                                         if (!allowDBNull)
705                                                                                 col.AllowDBNull = false;
706                                                                 }
707
708                                                                 if (columnType == DbTypes.TypeOfString) {
709                                                                         col.MaxLength = (ColumnSizeCol != null) ? (int)schemaRow[ColumnSizeCol] : 0;
710                                                                 }
711
712                                                                 if (isReadOnly)
713                                                                         col.ReadOnly = true;
714                                                                         
715                                                                 if (!allowDBNull && (!isReadOnly || isKey))
716                                                                         col.AllowDBNull = false;
717                                                                 if (isUnique && !isKey && !columnType.IsArray) {
718                                                                         col.Unique = true;
719                                                                         if (!allowDBNull)
720                                                                                 col.AllowDBNull = false;
721                                                                 }
722                                                         }
723
724                                                         if (isKey) {
725                                                                 primaryKey.Add (col);
726                                                                 if (allowDBNull)
727                                                                         createPrimaryKey = false;
728                                                         }
729                                                         
730                                                         // add the ordinal of the column as a key and the index of the column in the datareader as a value.
731                                                         mapping[col.Ordinal] = readerIndex++;
732                                                 }
733                                         }
734                                 }
735                         }
736                         if (primaryKey.Count > 0) {
737                                 DataColumn[] colKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
738                                 if (createPrimaryKey)
739                                         table.PrimaryKey = colKey;
740                                 else {
741                                         UniqueConstraint uConstraint = new UniqueConstraint(colKey);
742                                         for (int i = 0; i < table.Constraints.Count; i++) {
743                                                 if (table.Constraints[i].Equals(uConstraint)) {
744                                                         uConstraint = null;
745                                                         break;
746                                                 }
747                                         }
748
749                                         if (uConstraint != null)
750                                                 table.Constraints.Add(uConstraint);
751                                 }
752                         }
753
754                         return mapping;
755                         
756                 }
757
758                 [MonoTODO]
759                 object ICloneable.Clone ()
760                 {
761                         throw new NotImplementedException ();
762                 }
763
764                 [MonoTODO]
765                 public int Update (DataRow[] dataRows) 
766                 {
767                         if (dataRows == null)
768                                 throw new ArgumentNullException("dataRows");
769                         
770                         if (dataRows.Length == 0)
771                                 return 0;
772
773                         if (dataRows[0] == null)
774                                 throw new ArgumentException("dataRows[0].");
775
776                         DataTable table = dataRows[0].Table;
777                         if (table == null)
778                                 throw new ArgumentException("table is null reference.");
779                         
780                         // all rows must be in the same table
781                         for (int i = 0; i < dataRows.Length; i++)
782                         {
783                                 if (dataRows[i] == null)
784                                         throw new ArgumentException("dataRows[" + i + "].");
785                                 if (dataRows[i].Table != table)
786                                         throw new ArgumentException(
787                                                                     " DataRow["
788                                                                     + i
789                                                                     + "] is from a different DataTable than DataRow[0].");
790                         }
791                         
792                         // get table mapping for this rows
793                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
794                         if (tableMapping == null)
795                         {
796                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
797                                                                                                         TableMappings,
798                                                                                                         table.TableName,
799                                                                                                         table.TableName,
800                                                                                                         MissingMappingAction);
801                                 if (tableMapping != null) {
802                                         foreach (DataColumn col in table.Columns) {
803                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
804                                                         continue;
805                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
806                                                 if (columnMapping == null)
807                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
808                                                 tableMapping.ColumnMappings.Add (columnMapping);
809                                         }
810                                 } else {
811                                         ArrayList cmc = new ArrayList ();
812                                         foreach (DataColumn col in table.Columns)
813                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
814                                         tableMapping =
815                                                 new DataTableMapping (
816                                                                       table.TableName,
817                                                                       table.TableName,
818                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
819                                 }
820                         }
821
822                         DataRow[] copy = table.NewRowArray(dataRows.Length);
823                         Array.Copy(dataRows, 0, copy, 0, dataRows.Length);
824                         return Update(copy, tableMapping);
825                 }
826
827                 public override int Update (DataSet dataSet) 
828                 {
829                         return Update (dataSet, DefaultSourceTableName);
830                 }
831
832                 public int Update (DataTable dataTable) 
833                 {
834                         /*
835                           int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
836                           if (index < 0)
837                           throw new ArgumentException ();
838                           return Update (dataTable, TableMappings [index]);
839                         */
840                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable (dataTable.TableName);
841                         if (tableMapping == null)
842                         {
843                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (
844                                                                                                          TableMappings,
845                                                                                                          dataTable.TableName,
846                                                                                                          dataTable.TableName,
847                                                                                                          MissingMappingAction);
848                                 if (tableMapping != null) {
849                                         foreach (DataColumn col in dataTable.Columns) {
850                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
851                                                         continue;
852                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
853                                                 if (columnMapping == null)
854                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
855                                                 tableMapping.ColumnMappings.Add (columnMapping);
856                                         }
857                                 } else {
858                                         ArrayList cmc = new ArrayList ();
859                                         foreach (DataColumn col in dataTable.Columns)
860                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
861                                         tableMapping =
862                                                 new DataTableMapping (
863                                                                       dataTable.TableName,
864                                                                       dataTable.TableName,
865                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
866                                 }
867                         }
868                         return Update (dataTable, tableMapping);
869                 }
870
871                 private int Update (DataTable dataTable, DataTableMapping tableMapping)
872                 {
873                         DataRow[] rows = dataTable.NewRowArray(dataTable.Rows.Count);
874                         dataTable.Rows.CopyTo (rows, 0);
875                         return Update (rows, tableMapping);
876                 }
877
878                 [MonoTODO]
879                 protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping) 
880                 {
881                         int updateCount = 0;
882                         foreach (DataRow row in dataRows) {
883                                 StatementType statementType = StatementType.Update;
884                                 IDbCommand command = null;
885                                 string commandName = String.Empty;
886
887                                 switch (row.RowState) {
888                                 case DataRowState.Added:
889                                         statementType = StatementType.Insert;
890                                         command = InsertCommand;
891                                         commandName = "Insert";
892                                         break;
893                                 case DataRowState.Deleted:
894                                         statementType = StatementType.Delete;
895                                         command = DeleteCommand;
896                                         commandName = "Delete";
897                                         break;
898                                 case DataRowState.Modified:
899                                         statementType = StatementType.Update;
900                                         command = UpdateCommand;
901                                         commandName = "Update";
902                                         break;
903                                 case DataRowState.Unchanged:
904                                 case DataRowState.Detached:
905                                         continue;
906                                 }
907
908                                 RowUpdatingEventArgs argsUpdating = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
909                                 row.RowError = null;
910                                 OnRowUpdating(argsUpdating);
911                                 switch(argsUpdating.Status) {
912                                 case UpdateStatus.Continue :
913                                         //continue in update operation
914                                         break;
915                                 case UpdateStatus.ErrorsOccurred :
916                                         if (argsUpdating.Errors == null) {
917                                                 argsUpdating.Errors = ExceptionHelper.RowUpdatedError();
918                                         }
919                                         row.RowError += argsUpdating.Errors.Message;
920                                         if (!ContinueUpdateOnError) {
921                                                 throw argsUpdating.Errors;
922                                         }
923                                         continue;
924                                 case UpdateStatus.SkipAllRemainingRows :
925                                         return updateCount;
926                                 case UpdateStatus.SkipCurrentRow :
927                                         updateCount++;
928                                         continue;
929                                 default :
930                                         throw ExceptionHelper.InvalidUpdateStatus(argsUpdating.Status);
931                                 }
932                                 command = argsUpdating.Command;                                 
933                                 try {
934                                         if (command != null) {
935                                                 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
936                                                 foreach (IDataParameter parameter in command.Parameters) {
937                                                         if ((parameter.Direction & ParameterDirection.Input) != 0) {
938                                                                 string dsColumnName = parameter.SourceColumn;
939                                                                 if (columnMappings.Contains(parameter.SourceColumn))
940                                                                         dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
941                                                                 if (dsColumnName == null || dsColumnName.Length <= 0)
942                                                                         continue;
943                                                                 
944                                                                 DataRowVersion rowVersion = parameter.SourceVersion;
945                                                                 // Parameter version is ignored for non-update commands
946                                                                 if (statementType == StatementType.Delete) 
947                                                                         rowVersion = DataRowVersion.Original;
948
949                                                                 parameter.Value = row [dsColumnName, rowVersion];
950                                                         }
951                                                 }
952                                         }
953                                 }
954                                 catch (Exception e) {
955                                         argsUpdating.Errors = e;
956                                         argsUpdating.Status = UpdateStatus.ErrorsOccurred;
957                                 }
958
959                                 
960                                 IDataReader reader = null;
961                                 try {                                                           
962                                         if (command == null) {
963                                                 throw ExceptionHelper.UpdateRequiresCommand(commandName);
964                                         }                               
965                                 
966                                         CommandBehavior commandBehavior = CommandBehavior.Default;
967                                         if (command.Connection.State == ConnectionState.Closed) {
968                                                 command.Connection.Open ();
969                                                 commandBehavior |= CommandBehavior.CloseConnection;
970                                         }
971                                 
972                                         // use ExecuteReader because we want to use the commandbehavior parameter.
973                                         // so the connection will be closed if needed.
974                                         reader = command.ExecuteReader (commandBehavior);
975
976                                         // update the current row, if the update command returns any resultset
977                                         // ignore other than the first record.
978                                         DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
979
980                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
981                                             command.UpdatedRowSource == UpdateRowSource.FirstReturnedRecord) {
982                                                 if (reader.Read ()){
983                                                         DataTable retSchema = reader.GetSchemaTable ();
984                                                         foreach (DataRow dr in retSchema.Rows) {
985                                                                 string columnName = dr ["ColumnName"].ToString ();
986                                                                 string dstColumnName = columnName;
987                                                                 if (columnMappings != null &&
988                                                                     columnMappings.Contains(columnName))
989                                                                         dstColumnName = columnMappings [dstColumnName].DataSetColumn;
990                                                                 DataColumn dstColumn = row.Table.Columns [dstColumnName];
991                                                                 if (dstColumn == null
992                                                                     || (dstColumn.Expression != null
993                                                                         && dstColumn.Expression.Length > 0))
994                                                                         continue;
995                                                                 // info from : http://www.error-bank.com/microsoft.public.dotnet.framework.windowsforms.databinding/
996                                                                 // _35_hcsyiv0dha.2328@tk2msftngp10.phx.gbl_Thread.aspx
997                                                                 // disable readonly for non-expression columns.
998                                                                 bool readOnlyState = dstColumn.ReadOnly;
999                                                                 dstColumn.ReadOnly = false;
1000                                                                 try {
1001                                                                         row [dstColumnName] = reader [columnName];
1002                                                                 } finally {
1003                                                                         dstColumn.ReadOnly = readOnlyState;
1004                                                                 }                                    
1005                                                         }
1006                                                 }
1007                                         }
1008                                         reader.Close ();
1009
1010                                         int tmp = reader.RecordsAffected; // records affected is valid only after closing reader
1011                                         // if the execute does not effect any rows we throw an exception.
1012                                         if (tmp == 0)
1013                                                 throw new DBConcurrencyException("Concurrency violation: the " + 
1014                                                                                  commandName +"Command affected 0 records.");
1015                                         updateCount += tmp;
1016                                         
1017                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
1018                                             command.UpdatedRowSource == UpdateRowSource.OutputParameters) {
1019                                                 // Update output parameters to row values
1020                                                 foreach (IDataParameter parameter in command.Parameters) {
1021                                                         if (parameter.Direction != ParameterDirection.InputOutput
1022                                                             && parameter.Direction != ParameterDirection.Output
1023                                                             && parameter.Direction != ParameterDirection.ReturnValue)
1024                                                                 continue;
1025
1026                                                         string dsColumnName = parameter.SourceColumn;
1027                                                         if (columnMappings != null &&
1028                                                             columnMappings.Contains(parameter.SourceColumn))
1029                                                                 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
1030                                                         DataColumn dstColumn = row.Table.Columns [dsColumnName];
1031                                                         if (dstColumn == null
1032                                                             || (dstColumn.Expression != null 
1033                                                                 && dstColumn.Expression.Length > 0))
1034                                                                 continue;
1035                                                         bool readOnlyState = dstColumn.ReadOnly;
1036                                                         dstColumn.ReadOnly  = false;
1037                                                         try {
1038                                                                 row [dsColumnName] = parameter.Value;
1039                                                         } finally {
1040                                                                 dstColumn.ReadOnly = readOnlyState;
1041                                                         }
1042                                     
1043                                                 }
1044                                         }
1045                     
1046                                         RowUpdatedEventArgs updatedArgs = CreateRowUpdatedEvent(row, command, statementType, tableMapping);
1047                                         OnRowUpdated(updatedArgs);
1048                                         switch(updatedArgs.Status) {
1049                                         case UpdateStatus.Continue:
1050                                                 break;
1051                                         case UpdateStatus.ErrorsOccurred:
1052                                                 if (updatedArgs.Errors == null) {
1053                                                         updatedArgs.Errors = ExceptionHelper.RowUpdatedError();
1054                                                 }
1055                                                 row.RowError += updatedArgs.Errors.Message;
1056                                                 if (!ContinueUpdateOnError) {
1057                                                         throw updatedArgs.Errors;
1058                                                 }
1059                                                 break;
1060                                         case UpdateStatus.SkipCurrentRow:
1061                                                 continue;
1062                                         case UpdateStatus.SkipAllRemainingRows:
1063                                                 return updateCount;
1064                                         }
1065                                         row.AcceptChanges ();
1066                                 } catch(Exception e) {
1067                                         row.RowError = e.Message;
1068                                         if (!ContinueUpdateOnError) {
1069                                                 throw e;
1070                                         }
1071                                 } finally {
1072                                         if (reader != null && ! reader.IsClosed) {
1073                                                 reader.Close ();
1074                                         }
1075                                 }       
1076                         }               
1077                         return updateCount;
1078                 }
1079
1080                 public int Update (DataSet dataSet, string sourceTable) 
1081                 {
1082                         MissingMappingAction mappingAction = MissingMappingAction;
1083
1084                         if (mappingAction == MissingMappingAction.Ignore)
1085                                 mappingAction = MissingMappingAction.Error;
1086
1087                         DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction);
1088
1089                         DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable];
1090                         if (dataTable == null)
1091                                 throw new ArgumentException (String.Format ("Missing table {0}",
1092                                                                             sourceTable));
1093                         return Update (dataTable, tableMapping);
1094                 }
1095
1096 #if ONLY_1_0 || ONLY_1_1
1097                 protected virtual void OnFillError (FillErrorEventArgs value) 
1098                 {
1099                         if (FillError != null)
1100                                 FillError (this, value);
1101                 }
1102 #endif
1103
1104                 protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
1105                 protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
1106                 
1107                 #endregion // Methods
1108         }
1109 }