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