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