New test.
[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 //   Veerapuram Varadhan  <vvaradhan@novell.com>
9 //
10 // (C) Ximian, Inc
11 // Copyright (C) Tim Coleman, 2002-2003
12 //
13
14 //
15 // Copyright (C) 2004, 2009 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Collections;
39 using System.ComponentModel;
40 using System.Data;
41 using System.Reflection;
42 using System.Runtime.InteropServices;
43
44 namespace System.Data.Common
45 {
46 #if NET_2_0
47         public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
48 #else
49         public abstract class DbDataAdapter : DataAdapter, ICloneable
50 #endif
51         {
52                 #region Fields
53
54                 public const string DefaultSourceTableName = "Table";
55                 const string DefaultSourceColumnName = "Column";
56                 CommandBehavior _behavior = CommandBehavior.Default;
57
58 #if NET_2_0
59                 IDbCommand _selectCommand;
60                 IDbCommand _updateCommand;
61                 IDbCommand _deleteCommand;
62                 IDbCommand _insertCommand;
63 #endif
64
65                 #endregion // Fields
66                 
67                 #region Constructors
68
69                 protected DbDataAdapter ()
70                 {
71                 }
72
73                 protected DbDataAdapter (DbDataAdapter adapter) : base (adapter)
74                 {
75                 }
76
77                 #endregion // Fields
78
79                 #region Properties
80
81 #if NET_2_0
82                 protected internal CommandBehavior FillCommandBehavior {
83                         get { return _behavior; }
84                         set { _behavior = value; }
85                 }
86
87                 IDbCommand IDbDataAdapter.SelectCommand {
88                     get { return ((DbDataAdapter)this).SelectCommand; }
89                     set { ((DbDataAdapter)this).SelectCommand = (DbCommand)value; }
90                 }
91
92                 IDbCommand IDbDataAdapter.UpdateCommand{
93                     get { return ((DbDataAdapter)this).UpdateCommand; }
94                     set { ((DbDataAdapter)this).UpdateCommand = (DbCommand)value; }
95                 }
96                 
97                 IDbCommand IDbDataAdapter.DeleteCommand{
98                     get { return ((DbDataAdapter)this).DeleteCommand; }
99                     set { ((DbDataAdapter)this).DeleteCommand = (DbCommand)value; }
100                 }
101
102                 IDbCommand IDbDataAdapter.InsertCommand{
103                     get { return ((DbDataAdapter)this).InsertCommand; }
104                     set { ((DbDataAdapter)this).InsertCommand = (DbCommand)value; }
105                 }
106                 
107                 [Browsable (false)]
108                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
109                 public DbCommand SelectCommand {
110                     get {
111                                         return (DbCommand) _selectCommand;
112                                         //return (DbCommand) ((IDbDataAdapter)this).SelectCommand; 
113                         }
114                     set {
115                                         if (_selectCommand != value) {
116                                                 _selectCommand = value;
117                                                 ((IDbDataAdapter)this).SelectCommand = value; 
118                                         }
119                         }
120                 }
121
122                 [Browsable (false)]
123                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
124                 public DbCommand DeleteCommand {
125                     get {
126                                         return (DbCommand) _deleteCommand;
127                                         //return (DbCommand) ((IDbDataAdapter)this).DeleteCommand; 
128                         }
129                     set {
130                                         if (_deleteCommand != value) {
131                                                 _deleteCommand = value;
132                                                 ((IDbDataAdapter)this).DeleteCommand = value; 
133                                         }
134                         }
135                 }
136
137                 [Browsable (false)]
138                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
139                 public DbCommand InsertCommand {
140                     get {
141                                         return (DbCommand) _insertCommand;
142                                         //return (DbCommand) ((IDbDataAdapter)this).InsertCommand; 
143                         }
144                     set {
145                                         if (_insertCommand != value) {
146                                                 _insertCommand = value;
147                                                 ((IDbDataAdapter)this).InsertCommand = value; 
148                                         }
149                         }
150                 }
151
152                 [Browsable (false)]
153                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
154                 public DbCommand UpdateCommand {
155                     get {
156                                         return (DbCommand) _updateCommand;
157                                         //return (DbCommand) ((IDbDataAdapter)this).DeleteCommand; 
158                         }
159                     set {
160                                         if (_updateCommand != value) {
161                                                 _updateCommand = value;
162                                                 ((IDbDataAdapter)this).UpdateCommand = value; 
163                                         }
164                         }
165                 }
166
167                 [DefaultValue (1)]
168                 public virtual int UpdateBatchSize {
169                         get { return 1; }
170                         set {
171                                 if (value != 1)
172                                         throw new NotSupportedException ();
173                         }
174                 }
175 #endif
176
177                 #endregion // Properties
178                 
179                 #region Events
180
181 #if ONLY_1_0 || ONLY_1_1
182                 [DataCategory ("Fill")]
183                 [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
184                 public event FillErrorEventHandler FillError;
185
186 #endif
187                 #endregion // Events
188
189                 #region Methods
190
191 #if NET_2_0
192                 protected virtual RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command,
193                                                                              StatementType statementType,
194                                                                              DataTableMapping tableMapping)
195                 {
196                         return new RowUpdatedEventArgs (dataRow, command, statementType, tableMapping);
197                 }
198
199                 protected virtual RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command,
200                                                                                StatementType statementType,
201                                                                                DataTableMapping tableMapping)
202                 {
203                         return new RowUpdatingEventArgs (dataRow, command, statementType, tableMapping);
204                 }
205
206                 protected virtual void OnRowUpdated (RowUpdatedEventArgs value)
207                 {
208                         if (Events ["RowUpdated"] != null) {
209                                 Delegate [] rowUpdatedList = Events ["RowUpdated"].GetInvocationList ();
210                                 foreach (Delegate rowUpdated in rowUpdatedList) {
211                                         MethodInfo rowUpdatedMethod = rowUpdated.Method;
212                                         rowUpdatedMethod.Invoke (value, null);
213                                 }
214                         }
215                 }
216
217                 protected virtual void OnRowUpdating (RowUpdatingEventArgs value)
218                 {
219                         if (Events ["RowUpdating"] != null) {
220                                 Delegate [] rowUpdatingList = Events ["RowUpdating"].GetInvocationList ();
221                                 foreach (Delegate rowUpdating in rowUpdatingList) {
222                                         MethodInfo rowUpdatingMethod = rowUpdating.Method;
223                                         rowUpdatingMethod.Invoke (value, null);
224                                 }
225                         }
226                 }
227 #else
228                 protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command,
229                                                                              StatementType statementType,
230                                                                              DataTableMapping tableMapping);
231
232                 protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command,
233                                                                                StatementType statementType,
234                                                                                DataTableMapping tableMapping);
235
236                 protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
237                 protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
238 #endif
239
240                 protected override void Dispose (bool disposing)
241                 {
242                         if (disposing) {
243                                 IDbDataAdapter da = (IDbDataAdapter) this;
244                                 if (da.SelectCommand != null) {
245                                         da.SelectCommand.Dispose();
246                                         da.SelectCommand = null;
247                                 }
248                                 if (da.InsertCommand != null) {
249                                         da.InsertCommand.Dispose();
250                                         da.InsertCommand = null;
251                                 }
252                                 if (da.UpdateCommand != null) {
253                                         da.UpdateCommand.Dispose();
254                                         da.UpdateCommand = null;
255                                 }
256                                 if (da.DeleteCommand != null) {
257                                         da.DeleteCommand.Dispose();
258                                         da.DeleteCommand = null;
259                                 }
260                         }
261                 }
262
263                 public override int Fill (DataSet dataSet)
264                 {
265                         return Fill (dataSet, 0, 0, DefaultSourceTableName, ((IDbDataAdapter) this).SelectCommand, _behavior);
266                 }
267
268                 public int Fill (DataTable dataTable)
269                 {
270                         if (dataTable == null)
271                                 throw new ArgumentNullException ("DataTable");
272
273                         return Fill (dataTable, ((IDbDataAdapter) this).SelectCommand, _behavior);
274                 }
275
276                 public int Fill (DataSet dataSet, string srcTable)
277                 {
278                         return Fill (dataSet, 0, 0, srcTable, ((IDbDataAdapter) this).SelectCommand, _behavior);
279                 }
280
281 #if !NET_2_0
282                 protected virtual int Fill (DataTable dataTable, IDataReader dataReader)
283                 {
284                         return base.FillInternal (dataTable, dataReader);
285                 }
286 #endif
287
288                 protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
289                 {
290                         CommandBehavior commandBehavior = behavior;
291
292                         // first see that the connection is not close.
293                         if (command.Connection.State == ConnectionState.Closed) {
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, _behavior);
303                 }
304
305 #if NET_2_0
306                 [MonoTODO]
307                 public int Fill (int startRecord, int maxRecords, params 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 #else
318                 protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
319                 {
320                         return base.FillInternal (dataSet, srcTable, dataReader, startRecord, maxRecords);
321                 }
322 #endif
323
324                 protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
325                 {
326                         if (command.Connection == null)
327                                 throw new InvalidOperationException ("Connection state is closed");
328
329                         if (MissingSchemaAction == MissingSchemaAction.AddWithKey)
330                                 behavior |= CommandBehavior.KeyInfo;
331                         CommandBehavior commandBehavior = behavior;
332
333                         if (command.Connection.State == ConnectionState.Closed) {
334                                 command.Connection.Open ();
335                                 commandBehavior |= CommandBehavior.CloseConnection;
336                         }
337                         return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior),
338                                 startRecord, maxRecords);
339                 }
340
341 #if NET_2_0
342                 /// <summary>
343                 /// Fills the given datatable using values from reader. if a value 
344                 /// for a column is  null, that will be filled with default value. 
345                 /// </summary>
346                 /// <returns>No. of rows affected </returns>
347                 internal static int FillFromReader (DataTable table,
348                                                     IDataReader reader,
349                                                     int start,
350                                                     int length,
351                                                     int [] mapping,
352                                                     LoadOption loadOption
353                                                     )
354                 {
355                         if (reader.FieldCount == 0)
356                                 return 0 ;
357
358                         for (int i = 0; i < start; i++)
359                                 reader.Read ();
360
361                         int counter = 0;
362                         object [] values = new object [mapping.Length];
363                         while (reader.Read () && (length == 0 || counter < length)) {
364                                 for (int i = 0 ; i < mapping.Length; i++)
365                                         values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
366                                 table.BeginLoadData ();
367                                 table.LoadDataRow (values, loadOption);
368                                 table.EndLoadData ();
369                                 counter++;
370                         }
371                         return counter;
372                 }
373
374                 internal static int FillFromReader (DataTable table,
375                                                     IDataReader reader,
376                                                     int start,
377                                                     int length,
378                                                     int [] mapping,
379                                                     LoadOption loadOption,
380                                                     FillErrorEventHandler errorHandler)
381                 {
382                         if (reader.FieldCount == 0)
383                                 return 0 ;
384
385                         for (int i = 0; i < start; i++)
386                                 reader.Read ();
387
388                         int counter = 0;
389                         object [] values = new object [mapping.Length];
390                         while (reader.Read () && (length == 0 || counter < length)) {
391                                 for (int i = 0 ; i < mapping.Length; i++)
392                                         values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
393                                 table.BeginLoadData ();
394                                 try {
395                                         table.LoadDataRow (values, loadOption);
396                                 } catch (Exception e) {
397                                         FillErrorEventArgs args = new FillErrorEventArgs (table, values);
398                                         args.Errors = e;
399                                         args.Continue = false;
400                                         errorHandler (table, args);
401                                         // if args.Continue is not set to true or if a handler is not set, rethrow the error..
402                                         if(!args.Continue)
403                                                 throw e;
404                                 }
405                                 table.EndLoadData ();
406                                 counter++;
407                         }
408                         return counter;
409                 }
410 #endif // NET_2_0
411
412                 public override DataTable [] FillSchema (DataSet dataSet, SchemaType schemaType)
413                 {
414                         return FillSchema (dataSet, schemaType, ((IDbDataAdapter) this).SelectCommand, DefaultSourceTableName, _behavior);
415                 }
416
417                 public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
418                 {
419                         return FillSchema (dataTable, schemaType, ((IDbDataAdapter) this).SelectCommand, _behavior);
420                 }
421
422                 public DataTable [] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
423                 {
424                         return FillSchema (dataSet, schemaType, ((IDbDataAdapter) this).SelectCommand, srcTable, _behavior);
425                 }
426
427                 protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
428                 {
429                         if (dataTable == null)
430                                 throw new ArgumentNullException ("DataTable");
431
432                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
433                         if (command.Connection.State == ConnectionState.Closed) {
434                                 command.Connection.Open ();
435                                 behavior |= CommandBehavior.CloseConnection;
436                         }
437
438                         IDataReader reader = command.ExecuteReader (behavior);
439                         try {
440                                 string tableName =  SetupSchema (schemaType, dataTable.TableName);
441                                 if (tableName != null) {
442                                         // FillSchema should add the KeyInfo unless MissingSchemaAction
443                                         // is set to Ignore or Error.
444                                         MissingSchemaAction schemaAction = MissingSchemaAction;
445                                         if (!(schemaAction == MissingSchemaAction.Ignore ||
446                                                 schemaAction == MissingSchemaAction.Error))
447                                                 schemaAction = MissingSchemaAction.AddWithKey;
448
449                                         BuildSchema (reader, dataTable, schemaType, schemaAction,
450                                                 MissingMappingAction, TableMappings);
451                                 }
452                         } finally {
453                                 reader.Close ();
454                         }
455                         return dataTable;
456                 }
457
458                 protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
459                 {
460                         if (dataSet == null)
461                                 throw new ArgumentNullException ("DataSet");
462
463                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
464                         if (command.Connection.State == ConnectionState.Closed) {
465                                 command.Connection.Open ();
466                                 behavior |= CommandBehavior.CloseConnection;
467                         }
468
469                         IDataReader reader = command.ExecuteReader (behavior);
470                         ArrayList output = new ArrayList ();
471                         string tableName = srcTable;
472                         int index = 0;
473                         DataTable table;
474                         try {
475                                 // FillSchema should add the KeyInfo unless MissingSchemaAction
476                                 // is set to Ignore or Error.
477                                 MissingSchemaAction schemaAction = MissingSchemaAction;
478                                 if (!(MissingSchemaAction == MissingSchemaAction.Ignore ||
479                                         MissingSchemaAction == MissingSchemaAction.Error))
480                                         schemaAction = MissingSchemaAction.AddWithKey;
481
482                                 do {
483                                         tableName = SetupSchema (schemaType, tableName);
484                                         if (tableName != null) {
485                                                 if (dataSet.Tables.Contains (tableName))
486                                                         table = dataSet.Tables [tableName];
487                                                 else {
488                                                         // Do not create schema if MissingSchemAction is set to Ignore
489                                                         if (this.MissingSchemaAction == MissingSchemaAction.Ignore)
490                                                                 continue;
491                                                         table =  dataSet.Tables.Add (tableName);
492                                                 }
493                                                 
494                                                 BuildSchema (reader, table, schemaType, schemaAction,
495                                                         MissingMappingAction, TableMappings);
496                                                 output.Add (table);
497                                                 tableName = String.Format ("{0}{1}", srcTable, ++index);
498                                         }
499                                 }while (reader.NextResult ());
500                         } finally {
501                                 reader.Close ();
502                         }
503                         return (DataTable []) output.ToArray (typeof (DataTable));
504                 }
505
506                 [EditorBrowsable (EditorBrowsableState.Advanced)]
507                 public override IDataParameter[] GetFillParameters ()
508                 {
509                         IDbCommand selectCmd = ((IDbDataAdapter) this).SelectCommand;
510                         IDataParameter[] parameters = new IDataParameter [selectCmd.Parameters.Count];
511                         selectCmd.Parameters.CopyTo (parameters, 0);
512                         return parameters;
513                 }
514                 
515                 [MonoTODO]
516                 [Obsolete ("use 'protected DbDataAdapter(DbDataAdapter)' ctor")]
517                 object ICloneable.Clone ()
518                 {
519                         throw new NotImplementedException ();
520                 }
521
522                 public int Update (DataRow [] dataRows)
523                 {
524                         if (dataRows == null)
525                                 throw new ArgumentNullException("dataRows");
526                         
527                         if (dataRows.Length == 0)
528                                 return 0;
529
530                         if (dataRows [0] == null)
531                                 throw new ArgumentException("dataRows[0].");
532
533                         DataTable table = dataRows [0].Table;
534                         if (table == null)
535                                 throw new ArgumentException("table is null reference.");
536                         
537                         // all rows must be in the same table
538                         for (int i = 0; i < dataRows.Length; i++) {
539                                 if (dataRows [i] == null)
540                                         throw new ArgumentException ("dataRows[" + i + "].");
541                                 if (dataRows [i].Table != table)
542                                         throw new ArgumentException(
543                                                                     " DataRow["
544                                                                     + i
545                                                                     + "] is from a different DataTable than DataRow[0].");
546                         }
547                         
548                         // get table mapping for this rows
549                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
550                         if (tableMapping == null) {
551                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
552                                                                                                         TableMappings,
553                                                                                                         table.TableName,
554                                                                                                         table.TableName,
555                                                                                                         MissingMappingAction);
556                                 if (tableMapping != null) {
557                                         foreach (DataColumn col in table.Columns) {
558                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
559                                                         continue;
560                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
561                                                 if (columnMapping == null)
562                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
563                                                 tableMapping.ColumnMappings.Add (columnMapping);
564                                         }
565                                 } else {
566                                         ArrayList cmc = new ArrayList ();
567                                         foreach (DataColumn col in table.Columns)
568                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
569                                         tableMapping =
570                                                 new DataTableMapping (
571                                                                       table.TableName,
572                                                                       table.TableName,
573                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
574                                 }
575                         }
576
577                         DataRow[] copy = table.NewRowArray (dataRows.Length);
578                         Array.Copy (dataRows, 0, copy, 0, dataRows.Length);
579                         return Update (copy, tableMapping);
580                 }
581
582                 public override int Update (DataSet dataSet)
583                 {
584                         return Update (dataSet, DefaultSourceTableName);
585                 }
586
587                 public int Update (DataTable dataTable)
588                 {
589                         /*
590                           int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
591                           if (index < 0)
592                           throw new ArgumentException ();
593                           return Update (dataTable, TableMappings [index]);
594                         */
595                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable (dataTable.TableName);
596                         if (tableMapping == null) {
597                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (
598                                                                                                          TableMappings,
599                                                                                                          dataTable.TableName,
600                                                                                                          dataTable.TableName,
601                                                                                                          MissingMappingAction);
602                                 if (tableMapping != null) {
603                                         foreach (DataColumn col in dataTable.Columns) {
604                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
605                                                         continue;
606                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
607                                                 if (columnMapping == null)
608                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
609                                                 tableMapping.ColumnMappings.Add (columnMapping);
610                                         }
611                                 } else {
612                                         ArrayList cmc = new ArrayList ();
613                                         foreach (DataColumn col in dataTable.Columns)
614                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
615                                         tableMapping =
616                                                 new DataTableMapping (
617                                                                       dataTable.TableName,
618                                                                       dataTable.TableName,
619                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
620                                 }
621                         }
622                         return Update (dataTable, tableMapping);
623                 }
624
625                 private int Update (DataTable dataTable, DataTableMapping tableMapping)
626                 {
627                         DataRow [] rows = dataTable.NewRowArray(dataTable.Rows.Count);
628                         dataTable.Rows.CopyTo (rows, 0);
629                         return Update (rows, tableMapping);
630                 }
631
632                 protected virtual int Update (DataRow [] dataRows, DataTableMapping tableMapping)
633                 {
634                         int updateCount = 0;
635                         foreach (DataRow row in dataRows) {
636                                 StatementType statementType = StatementType.Update;
637                                 IDbCommand command = null;
638                                 string commandName = String.Empty;
639
640                                 switch (row.RowState) {
641                                 case DataRowState.Added:
642                                         statementType = StatementType.Insert;
643                                         command = ((IDbDataAdapter) this).InsertCommand;
644                                         commandName = "Insert";
645                                         break;
646                                 case DataRowState.Deleted:
647                                         statementType = StatementType.Delete;
648                                         command = ((IDbDataAdapter) this).DeleteCommand;
649                                         commandName = "Delete";
650                                         break;
651                                 case DataRowState.Modified:
652                                         statementType = StatementType.Update;
653                                         command = ((IDbDataAdapter) this).UpdateCommand;
654                                         commandName = "Update";
655                                         break;
656                                 case DataRowState.Unchanged:
657                                 case DataRowState.Detached:
658                                         continue;
659                                 }
660
661                                 RowUpdatingEventArgs argsUpdating = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
662                                 row.RowError = null;
663                                 OnRowUpdating (argsUpdating);
664                                 switch (argsUpdating.Status) {
665                                 case UpdateStatus.Continue :
666                                         //continue in update operation
667                                         break;
668                                 case UpdateStatus.ErrorsOccurred :
669                                         if (argsUpdating.Errors == null)
670                                                 argsUpdating.Errors = ExceptionHelper.RowUpdatedError();
671                                         row.RowError += argsUpdating.Errors.Message;
672                                         if (!ContinueUpdateOnError)
673                                                 throw argsUpdating.Errors;
674                                         continue;
675                                 case UpdateStatus.SkipAllRemainingRows :
676                                         return updateCount;
677                                 case UpdateStatus.SkipCurrentRow :
678                                         updateCount++;
679                                         continue;
680                                 default :
681                                         throw ExceptionHelper.InvalidUpdateStatus (argsUpdating.Status);
682                                 }
683                                 command = argsUpdating.Command;
684                                 try {
685                                         if (command != null) {
686                                                 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
687 #if ONLY_1_1
688                                                 IDataParameter nullCheckParam = null;
689 #endif
690                                                 foreach (IDataParameter parameter in command.Parameters) {
691                                                         if ((parameter.Direction & ParameterDirection.Input) == 0)
692                                                                 continue;
693
694                                                         DataRowVersion rowVersion = parameter.SourceVersion;
695                                                         // Parameter version is ignored for non-update commands
696                                                         if (statementType == StatementType.Delete)
697                                                                 rowVersion = DataRowVersion.Original;
698
699                                                         string dsColumnName = parameter.SourceColumn;
700 #if NET_2_0
701                                                         if (columnMappings.Contains(dsColumnName)) {
702                                                                 dsColumnName = columnMappings [dsColumnName].DataSetColumn;
703                                                                 parameter.Value = row [dsColumnName, rowVersion];
704                                                         } else {
705                                                                 parameter.Value = null;
706                                                         }
707
708                                                         DbParameter nullCheckParam = parameter as DbParameter;
709 #else
710                                                         if (columnMappings.Contains(dsColumnName))
711                                                                 dsColumnName = columnMappings [dsColumnName].DataSetColumn;
712                                                         if (dsColumnName == null || dsColumnName.Length == 0) {
713                                                                 nullCheckParam = parameter;
714                                                                 continue;
715                                                         }
716                                                         parameter.Value = row [dsColumnName, rowVersion];
717 #endif
718
719 #if NET_2_0
720                                                         if (nullCheckParam != null && nullCheckParam.SourceColumnNullMapping) {
721 #else
722                                                         if (nullCheckParam != null) {
723 #endif
724                                                                 if (parameter.Value != null && parameter.Value != DBNull.Value)
725                                                                         nullCheckParam.Value = 0;
726                                                                 else
727                                                                         nullCheckParam.Value = 1;
728                                                                 nullCheckParam = null;
729                                                         }
730                                                 }
731                                         }
732                                 } catch (Exception e) {
733                                         argsUpdating.Errors = e;
734                                         argsUpdating.Status = UpdateStatus.ErrorsOccurred;
735                                 }
736
737                                 IDataReader reader = null;
738                                 try {
739                                         if (command == null)
740                                                 throw ExceptionHelper.UpdateRequiresCommand (commandName);
741                                 
742                                         CommandBehavior commandBehavior = CommandBehavior.Default;
743                                         if (command.Connection.State == ConnectionState.Closed) {
744                                                 command.Connection.Open ();
745                                                 commandBehavior |= CommandBehavior.CloseConnection;
746                                         }
747                                 
748                                         // use ExecuteReader because we want to use the commandbehavior parameter.
749                                         // so the connection will be closed if needed.
750                                         reader = command.ExecuteReader (commandBehavior);
751
752                                         // update the current row, if the update command returns any resultset
753                                         // ignore other than the first record.
754                                         DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
755
756                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
757                                             command.UpdatedRowSource == UpdateRowSource.FirstReturnedRecord) {
758                                                 if (reader.Read ()){
759                                                         DataTable retSchema = reader.GetSchemaTable ();
760                                                         foreach (DataRow dr in retSchema.Rows) {
761                                                                 string columnName = dr ["ColumnName"].ToString ();
762                                                                 string dstColumnName = columnName;
763                                                                 if (columnMappings != null &&
764                                                                     columnMappings.Contains(columnName))
765                                                                         dstColumnName = columnMappings [dstColumnName].DataSetColumn;
766                                                                 DataColumn dstColumn = row.Table.Columns [dstColumnName];
767                                                                 if (dstColumn == null
768                                                                     || (dstColumn.Expression != null
769                                                                         && dstColumn.Expression.Length > 0))
770                                                                         continue;
771                                                                 // info from : http://www.error-bank.com/microsoft.public.dotnet.framework.windowsforms.databinding/
772                                                                 // _35_hcsyiv0dha.2328@tk2msftngp10.phx.gbl_Thread.aspx
773                                                                 // disable readonly for non-expression columns.
774                                                                 bool readOnlyState = dstColumn.ReadOnly;
775                                                                 dstColumn.ReadOnly = false;
776                                                                 try {
777                                                                         row [dstColumnName] = reader [columnName];
778                                                                 } finally {
779                                                                         dstColumn.ReadOnly = readOnlyState;
780                                                                 }
781                                                         }
782                                                 }
783                                         }
784                                         reader.Close ();
785
786                                         int tmp = reader.RecordsAffected; // records affected is valid only after closing reader
787                                         // if the execute does not effect any rows we throw an exception.
788                                         if (tmp == 0)
789                                                 throw new DBConcurrencyException("Concurrency violation: the " + 
790                                                         commandName +"Command affected 0 records.", null,
791                                                         new DataRow [] { row });
792                                         updateCount += tmp;
793
794                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
795                                             command.UpdatedRowSource == UpdateRowSource.OutputParameters) {
796                                                 // Update output parameters to row values
797                                                 foreach (IDataParameter parameter in command.Parameters) {
798                                                         if (parameter.Direction != ParameterDirection.InputOutput
799                                                             && parameter.Direction != ParameterDirection.Output
800                                                             && parameter.Direction != ParameterDirection.ReturnValue)
801                                                                 continue;
802
803                                                         string dsColumnName = parameter.SourceColumn;
804                                                         if (columnMappings != null &&
805                                                             columnMappings.Contains(parameter.SourceColumn))
806                                                                 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
807                                                         DataColumn dstColumn = row.Table.Columns [dsColumnName];
808                                                         if (dstColumn == null
809                                                             || (dstColumn.Expression != null 
810                                                                 && dstColumn.Expression.Length > 0))
811                                                                 continue;
812                                                         bool readOnlyState = dstColumn.ReadOnly;
813                                                         dstColumn.ReadOnly  = false;
814                                                         try {
815                                                                 row [dsColumnName] = parameter.Value;
816                                                         } finally {
817                                                                 dstColumn.ReadOnly = readOnlyState;
818                                                         }
819                                                 }
820                                         }
821
822                                         RowUpdatedEventArgs updatedArgs = CreateRowUpdatedEvent (row, command, statementType, tableMapping);
823                                         OnRowUpdated (updatedArgs);
824                                         switch (updatedArgs.Status) {
825                                         case UpdateStatus.Continue:
826                                                 break;
827                                         case UpdateStatus.ErrorsOccurred:
828                                                 if (updatedArgs.Errors == null)
829                                                         updatedArgs.Errors = ExceptionHelper.RowUpdatedError();
830                                                 row.RowError += updatedArgs.Errors.Message;
831                                                 if (!ContinueUpdateOnError)
832                                                         throw updatedArgs.Errors;
833                                                 break;
834                                         case UpdateStatus.SkipCurrentRow:
835                                                 continue;
836                                         case UpdateStatus.SkipAllRemainingRows:
837                                                 return updateCount;
838                                         }
839 #if NET_2_0
840                                         if (!AcceptChangesDuringUpdate)
841                                                 continue;
842 #endif
843                                         row.AcceptChanges ();
844                                 } catch (Exception e) {
845                                         row.RowError = e.Message;
846                                         if (!ContinueUpdateOnError)
847                                                 throw e;
848                                 } finally {
849                                         if (reader != null && ! reader.IsClosed)
850                                                 reader.Close ();
851                                 }
852                         }
853                         return updateCount;
854                 }
855
856                 public int Update (DataSet dataSet, string srcTable)
857                 {
858                         MissingMappingAction mappingAction = MissingMappingAction;
859
860                         if (mappingAction == MissingMappingAction.Ignore)
861                                 mappingAction = MissingMappingAction.Error;
862
863                         DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, srcTable, srcTable, mappingAction);
864
865                         DataTable dataTable = dataSet.Tables [tableMapping.DataSetTable];
866                         if (dataTable == null)
867                                 throw new ArgumentException (String.Format ("Missing table {0}",
868                                                                             srcTable));
869                         return Update (dataTable, tableMapping);
870                 }
871
872 #if NET_2_0
873                 // All the batch methods, should be implemented, if supported,
874                 // by individual providers
875
876                 protected virtual int AddToBatch (IDbCommand command)
877                 {
878                         throw CreateMethodNotSupportedException ();
879                 }
880
881                 protected virtual void ClearBatch ()
882                 {
883                         throw CreateMethodNotSupportedException ();
884                 }
885
886                 protected virtual int ExecuteBatch ()
887                 {
888                         throw CreateMethodNotSupportedException ();
889                 }
890
891                 protected virtual IDataParameter GetBatchedParameter (int commandIdentifier, int parameterIndex)
892                 {
893                         throw CreateMethodNotSupportedException ();
894                 }
895
896                 protected virtual bool GetBatchedRecordsAffected (int commandIdentifier, out int recordsAffected, out Exception error)
897                 {
898                         recordsAffected = 1;
899                         error = null;
900                         return true;
901                 }
902
903                 protected virtual void InitializeBatching ()
904                 {
905                         throw CreateMethodNotSupportedException ();
906                 }
907
908                 protected virtual void TerminateBatching ()
909                 {
910                         throw CreateMethodNotSupportedException ();
911                 }
912
913                 Exception CreateMethodNotSupportedException ()
914                 {
915                         return new NotSupportedException ("Method is not supported.");
916                 }
917 #else
918                 internal override void OnFillErrorInternal (FillErrorEventArgs value)
919                 {
920                         OnFillError (value);
921                 }
922
923                 protected virtual void OnFillError (FillErrorEventArgs value)
924                 {
925                         if (FillError != null)
926                                 FillError (this, value);
927                 }
928 #endif
929                 #endregion // Methods
930         }
931 }