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