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