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