ae3262f24daa74e1629e4e38eb17918770a71cc8
[mono.git] / mcs / class / System.Data / System.Data.Common / DbCommandBuilder.cs
1 //
2 // System.Data.Common.DbCommandBuilder
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2003
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 #if NET_2_0 || TARGET_JVM
34
35 using System.ComponentModel;
36 using System.Data;
37 using System.Globalization;
38 using System.Text;
39
40 namespace System.Data.Common {
41         public abstract class DbCommandBuilder : Component
42         {
43                 bool _setAllValues;
44                 bool _disposed;
45
46                 DataTable _dbSchemaTable;
47                 DbDataAdapter _dbDataAdapter;
48                 private CatalogLocation _catalogLocation = CatalogLocation.Start;
49                 private ConflictOption _conflictOption = ConflictOption.CompareAllSearchableValues;
50
51                 private string _tableName;
52                 private string _catalogSeparator;
53                 private string _quotePrefix;
54                 private string _quoteSuffix;
55                 private string _schemaSeparator;
56                 private DbCommand _dbCommand;
57
58                 DbCommand _deleteCommand;
59                 DbCommand _insertCommand;
60                 DbCommand _updateCommand;
61
62                 static readonly string SEPARATOR_DEFAULT = ".";
63                 // Used to construct WHERE clauses
64                 static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
65                 static readonly string clause2 = "({0} = {1})";
66
67                 protected DbCommandBuilder ()
68                 {
69                 }
70
71                 private void BuildCache (bool closeConnection)
72                 {
73                         DbCommand sourceCommand = SourceCommand;
74                         if (sourceCommand == null)
75                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
76                         DbConnection connection = sourceCommand.Connection;
77                         if (connection == null)
78                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
79
80                         if (_dbSchemaTable == null) {
81                                 if (connection.State == ConnectionState.Open)
82                                         closeConnection = false;        
83                                 else
84                                         connection.Open ();
85         
86                                 DbDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
87                                 _dbSchemaTable = reader.GetSchemaTable ();
88                                 reader.Close ();
89                                 if (closeConnection)
90                                         connection.Close ();    
91                                 BuildInformation (_dbSchemaTable);
92                         }
93                 }
94                 
95                 private string QuotedTableName {
96                         get { return GetQuotedString (_tableName); }
97                 }
98
99                 bool IsCommandGenerated {
100                         get {
101                                 return (_insertCommand != null || _updateCommand != null || _deleteCommand != null);
102                         }
103                 }
104
105                 private string GetQuotedString (string value)
106                 {
107                         if (value == String.Empty || value == null)
108                                 return value;
109
110                         string prefix = QuotePrefix;
111                         string suffix = QuoteSuffix;
112
113                         if (prefix.Length == 0 && suffix.Length == 0)
114                                 return value;
115                         return String.Format ("{0}{1}{2}", prefix, value, suffix);
116                 }
117
118                 private void BuildInformation (DataTable schemaTable)
119                 {
120                         _tableName = String.Empty;
121                         foreach (DataRow schemaRow in schemaTable.Rows) {
122                                 if (schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty)
123                                         continue;
124
125                                 if (_tableName == String.Empty) 
126                                         _tableName = (string) schemaRow ["BaseTableName"];
127                                 else if (_tableName != (string) schemaRow["BaseTableName"])
128                                         throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
129                         }
130                         if (_tableName == String.Empty)
131                                 throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
132                         _dbSchemaTable = schemaTable;
133                 }
134
135                 private bool IncludedInInsert (DataRow schemaRow)
136                 {
137                         // If the parameter has one of these properties, then we don't include it in the insert:
138                         // AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
139
140                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
141                                 return false;
142 //                      if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
143 //                              return false;
144                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
145                                 return false;
146                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
147                                 return false;
148                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
149                                 return false;
150                         return true;
151                 }
152
153                 private bool IncludedInUpdate (DataRow schemaRow)
154                 {
155                         // If the parameter has one of these properties, then we don't include it in the insert:
156                         // AutoIncrement, Hidden, RowVersion
157
158                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
159                                 return false;
160 //                      if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
161 //                              return false;
162                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
163                                 return false;
164                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
165                                 return false;
166                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
167                                 return false;
168
169                         return true;
170                 }
171
172                 private bool IncludedInWhereClause (DataRow schemaRow)
173                 {
174                         if ((bool) schemaRow ["IsLong"])
175                                 return false;
176                         return true;
177                 }
178
179                 private DbCommand CreateDeleteCommand (bool option)
180                 {
181                         // If no table was found, then we can't do an delete
182                         if (QuotedTableName == String.Empty)
183                                 return null;
184                         
185                         CreateNewCommand (ref _deleteCommand);
186
187                         string command = String.Format ("DELETE FROM {0}", QuotedTableName);
188                         StringBuilder whereClause = new StringBuilder ();
189                         bool keyFound = false;
190                         int parmIndex = 1;
191
192                         foreach (DataRow schemaRow in _dbSchemaTable.Rows) {
193                                 if (!schemaRow.IsNull ("IsExpression") && (bool)schemaRow["IsExpression"] == true)
194                                         continue;
195                                 if (!IncludedInWhereClause (schemaRow)) 
196                                         continue;
197
198                                 if (whereClause.Length > 0) 
199                                         whereClause.Append (" AND ");
200
201                                 bool isKey = (bool) schemaRow ["IsKey"];
202                                 DbParameter parameter = null;
203                                 string sourceColumnName;
204
205                                 if (isKey)
206                                         keyFound = true;
207
208                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
209                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
210                                 //following the 2.0 approach
211                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
212                                 if (!isKey && allowNull) {
213                                         parameter = _deleteCommand.CreateParameter ();
214                                         if (option) {
215                                                 parameter.ParameterName = String.Format ("@IsNull_{0}",
216                                                                                          schemaRow ["BaseColumnName"]);
217                                         } else {
218                                                 parameter.ParameterName = String.Format ("@p{0}", parmIndex++);
219                                         }
220                                         parameter.Value = 1;
221                                         parameter.DbType = DbType.Int32;
222                                         // This should be set for nullcheckparam
223                                         sourceColumnName = (string) schemaRow ["BaseColumnName"];
224                                         parameter.SourceColumn = sourceColumnName;
225                                         parameter.SourceColumnNullMapping = true;
226                                         parameter.SourceVersion = DataRowVersion.Original;
227                                         _deleteCommand.Parameters.Add (parameter);
228                                         
229                                         whereClause.Append ("(");
230                                         whereClause.Append (String.Format (clause1, parameter.ParameterName, 
231                                                                            GetQuotedString (sourceColumnName)));
232                                         whereClause.Append (" OR ");
233                                 }
234
235                                 if (option)
236                                         parameter = CreateParameter (_deleteCommand, schemaRow, true);
237                                 else 
238                                         parameter = CreateParameter (_deleteCommand, parmIndex++, schemaRow);
239                                 
240                                 parameter.SourceVersion = DataRowVersion.Original;
241                                 ApplyParameterInfo (parameter, schemaRow, StatementType.Delete, true);
242                                 //parameter.IsNullable = allowNull;
243
244                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
245
246                                 if (!isKey && allowNull)
247                                         whereClause.Append (")");
248                         }
249                         if (!keyFound)
250                                 throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
251
252                         // We're all done, so bring it on home
253                         string sql = String.Format ("{0} WHERE ({1})", command, whereClause.ToString ());
254                         _deleteCommand.CommandText = sql;
255                         _dbCommand = _deleteCommand;
256                         return _deleteCommand;
257                 }
258
259                 private DbCommand CreateInsertCommand (bool option, DataRow row)
260                 {
261                         if (QuotedTableName == String.Empty)
262                                 return null;
263
264                         CreateNewCommand (ref _insertCommand);
265
266                         string command = String.Format ("INSERT INTO {0}", QuotedTableName);
267                         string sql;
268                         StringBuilder columns = new StringBuilder ();
269                         StringBuilder values = new StringBuilder ();
270
271                         int parmIndex = 1;
272                         DbParameter parameter = null;
273                         foreach (DataRow schemaRow in _dbSchemaTable.Rows) {
274                                 if (!IncludedInInsert (schemaRow))
275                                         continue;
276
277                                 if (columns.Length > 0) {
278                                         columns.Append (", ");
279                                         values.Append (", ");
280                                 }
281
282                                 if (option)
283                                         parameter = CreateParameter (_insertCommand, schemaRow, false);
284                                 else 
285                                         parameter = CreateParameter (_insertCommand, parmIndex++, schemaRow);                   
286                                 parameter.SourceVersion = DataRowVersion.Current;
287                                 ApplyParameterInfo (parameter, schemaRow, StatementType.Insert, false);
288                                 
289                                 columns.Append (GetQuotedString (parameter.SourceColumn));
290
291                                 // Workaround for columns that may have a default/bound value and for now, 
292                                 // the framework, don't provide a mechanism to read these values yet
293                                 // AllowDBNull and DataRow is used to workaround #385028 by using DEFAULT 
294                                 string colName = schemaRow ["ColumnName"] as string;
295                                 bool allowDBNull = !schemaRow.IsNull ("AllowDBNull") & (bool) schemaRow ["AllowDBNull"];
296                                 if (!allowDBNull && row != null &&
297                                     (row [colName] == DBNull.Value || row [colName] == null)) {
298                                         values.Append ("DEFAULT");
299                                 } else {
300                                         values.Append (parameter.ParameterName);
301                                 }
302                         }
303
304                         sql = String.Format ("{0} ({1}) VALUES ({2})", command, columns.ToString (), values.ToString ());
305                         _insertCommand.CommandText = sql;
306                         _dbCommand = _insertCommand;
307                         return _insertCommand;
308                 }
309
310                 private void CreateNewCommand (ref DbCommand command)
311                 {
312                         DbCommand sourceCommand = SourceCommand;
313                         if (command == null) {
314                                 command = sourceCommand.Connection.CreateCommand ();
315                                 command.CommandTimeout = sourceCommand.CommandTimeout;
316                                 command.Transaction = sourceCommand.Transaction;
317                         }
318                         command.CommandType = CommandType.Text;
319                         command.UpdatedRowSource = UpdateRowSource.None;
320                         command.Parameters.Clear ();
321                 }
322
323                 private DbCommand CreateUpdateCommand (bool option)
324                 {
325                         // If no table was found, then we can't do an update
326                         if (QuotedTableName == String.Empty)
327                                 return null;
328
329                         CreateNewCommand (ref _updateCommand);
330
331                         string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
332                         StringBuilder columns = new StringBuilder ();
333                         StringBuilder whereClause = new StringBuilder ();
334                         int parmIndex = 1;
335                         bool keyFound = false;
336                         DbParameter parameter = null;
337
338                         // First, create the X=Y list for UPDATE
339                         foreach (DataRow schemaRow in _dbSchemaTable.Rows) {
340                                 if (!IncludedInUpdate (schemaRow))
341                                         continue;
342                                 if (columns.Length > 0) 
343                                         columns.Append (", ");
344
345                                 if (option)
346                                         parameter = CreateParameter (_updateCommand, schemaRow, false);
347                                 else 
348                                         parameter = CreateParameter (_updateCommand, parmIndex++, schemaRow);
349                                 parameter.SourceVersion = DataRowVersion.Current;
350                                 ApplyParameterInfo (parameter, schemaRow, StatementType.Update, false);
351                                 //parameter.IsNullable = (bool) schemaRow ["AllowDBNull"];
352                                 columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
353                         }
354
355                         // Now, create the WHERE clause.  This may be optimizable, but it would be ugly to incorporate
356                         // into the loop above.  "Premature optimization is the root of all evil." -- Knuth
357                         foreach (DataRow schemaRow in _dbSchemaTable.Rows) {
358                                 if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"] == true)
359                                         continue;
360
361                                 if (!IncludedInWhereClause (schemaRow)) 
362                                         continue;
363
364                                 if (whereClause.Length > 0) 
365                                         whereClause.Append (" AND ");
366
367                                 bool isKey = (bool) schemaRow ["IsKey"];
368                                 if (isKey)
369                                         keyFound = true;
370
371                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
372                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
373                                 //following the 2.0 approach
374                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
375                                 if (!isKey && allowNull) {
376                                         parameter = _updateCommand.CreateParameter ();
377                                         if (option) {
378                                                 parameter.ParameterName = String.Format ("@IsNull_{0}",
379                                                                                          schemaRow ["BaseColumnName"]);
380                                         } else {
381                                                 parameter.ParameterName = String.Format ("@p{0}", parmIndex++);
382                                         }
383                                         parameter.DbType = DbType.Int32;
384                                         parameter.Value = 1;
385                                         parameter.SourceColumn = (string) schemaRow ["BaseColumnName"];
386                                         parameter.SourceColumnNullMapping = true;
387                                         parameter.SourceVersion = DataRowVersion.Original;
388                                         whereClause.Append ("(");
389                                         whereClause.Append (String.Format (clause1, parameter.ParameterName,
390                                                                            GetQuotedString ((string) schemaRow ["BaseColumnName"])));
391                                         whereClause.Append (" OR ");
392                                         _updateCommand.Parameters.Add (parameter);
393                                 }
394
395                                 if (option)
396                                         parameter = CreateParameter (_updateCommand, schemaRow, true);
397                                 else 
398                                         parameter = CreateParameter (_updateCommand, parmIndex++, schemaRow);
399                                 parameter.SourceVersion = DataRowVersion.Original;
400                                 //parameter.IsNullable = allowNull;
401                                 ApplyParameterInfo (parameter, schemaRow, StatementType.Update, true);
402
403                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
404
405                                 if (!isKey && allowNull)
406                                         whereClause.Append (")");
407                         }
408                         if (!keyFound)
409                                 throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
410
411                         // We're all done, so bring it on home
412                         string sql = String.Format ("{0}{1} WHERE ({2})", command, columns.ToString (), whereClause.ToString ());
413                         _updateCommand.CommandText = sql;
414                         _dbCommand = _updateCommand;
415                         return _updateCommand;
416                 }
417
418                 private DbParameter CreateParameter (DbCommand _dbCommand, DataRow schemaRow, bool whereClause)
419                 {
420                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
421                         DbParameter parameter = _dbCommand.CreateParameter ();
422                         if (whereClause)
423                                 parameter.ParameterName = GetParameterName ("Original_" + sourceColumn);
424                         else
425                                 parameter.ParameterName = GetParameterName (sourceColumn);
426                         parameter.SourceColumn = sourceColumn;
427                         //parameter.Size = (int) schemaRow ["ColumnSize"];
428                         _dbCommand.Parameters.Add (parameter);
429                         return parameter;
430                 }
431
432                 private DbParameter CreateParameter (DbCommand _dbCommand, int paramIndex, DataRow schemaRow)
433                 {
434                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
435                         DbParameter parameter = _dbCommand.CreateParameter ();
436                         parameter.ParameterName = GetParameterName (paramIndex);
437                         parameter.SourceColumn = sourceColumn;
438                         //parameter.Size = (int) schemaRow ["ColumnSize"];
439                         _dbCommand.Parameters.Add (parameter);
440                         return parameter;
441                 }
442                 
443                 [DefaultValue (CatalogLocation.Start)]
444                 public virtual CatalogLocation CatalogLocation {
445                         get { return _catalogLocation; }
446                         set {
447                                 CheckEnumValue (typeof (CatalogLocation),
448                                         (int) value);
449                                 _catalogLocation = value;
450                         }
451                 }
452
453                 [DefaultValue (".")]
454                 public virtual string CatalogSeparator {
455                         get {
456                                 if (_catalogSeparator == null || _catalogSeparator.Length == 0)
457                                         return SEPARATOR_DEFAULT;
458                                 return _catalogSeparator;
459                         }
460                         set { _catalogSeparator = value; }
461                 }
462
463                 [DefaultValue (ConflictOption.CompareAllSearchableValues)]
464                 public virtual ConflictOption ConflictOption {
465                         get { return _conflictOption; }
466                         set {
467                                 CheckEnumValue (typeof (ConflictOption),
468                                         (int) value);
469                                 _conflictOption = value;
470                         }
471                 }
472
473                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
474                 [Browsable (false)]
475                 public DbDataAdapter DataAdapter {
476                         get { return _dbDataAdapter; }
477                         set {  if (value != null) 
478                                 SetRowUpdatingHandler (value);
479                                 _dbDataAdapter = value; 
480                         }
481                 }
482
483                 [DefaultValue ("")]
484                 public virtual string QuotePrefix {
485                         get {
486                                 if (_quotePrefix == null)
487                                         return string.Empty;
488                                 return _quotePrefix;
489                         }
490                         set {
491                                 if (IsCommandGenerated)
492                                         throw new InvalidOperationException (
493                                                 "QuotePrefix cannot be set after " +
494                                                 "an Insert, Update or Delete command " +
495                                                 "has been generated.");
496                                 _quotePrefix = value;
497                         }
498                 }
499
500                 [DefaultValue ("")]
501                 public virtual string QuoteSuffix {
502                         get {
503                                 if (_quoteSuffix == null)
504                                         return string.Empty;
505                                 return _quoteSuffix;
506                         }
507                         set {
508                                 if (IsCommandGenerated)
509                                         throw new InvalidOperationException (
510                                                 "QuoteSuffix cannot be set after " +
511                                                 "an Insert, Update or Delete command " +
512                                                 "has been generated.");
513                                 _quoteSuffix = value;
514                         }
515                 }
516
517                 [DefaultValue (".")]
518                 public virtual string SchemaSeparator {
519                         get {
520                                 if (_schemaSeparator == null || _schemaSeparator.Length == 0)
521                                         return SEPARATOR_DEFAULT;
522                                 return _schemaSeparator;
523                         }
524                         set { _schemaSeparator = value; }
525                 }
526
527                 [DefaultValue (false)]
528                 public bool SetAllValues {
529                         get { return _setAllValues; }
530                         set { _setAllValues = value; }
531                 }
532
533                 private DbCommand SourceCommand {
534                         get {
535                                 if (_dbDataAdapter != null)
536                                         return _dbDataAdapter.SelectCommand;
537                                 return null;
538                         }
539                 }
540
541                 protected abstract void ApplyParameterInfo (DbParameter parameter, 
542                                                             DataRow row, 
543                                                             StatementType statementType, 
544                                                             bool whereClause);
545
546                 protected override void Dispose (bool disposing)
547                 {
548                         if (!_disposed) {
549                                 if (disposing) {
550                                         if (_insertCommand != null)
551                                                 _insertCommand.Dispose ();
552                                         if (_deleteCommand != null)
553                                                 _deleteCommand.Dispose ();
554                                         if (_updateCommand != null)
555                                                 _updateCommand.Dispose ();
556                                         if (_dbSchemaTable != null)
557                                                 _dbSchemaTable.Dispose ();
558                                 }
559                                 _disposed = true;
560                         }
561                 }
562
563                 public DbCommand GetDeleteCommand ()
564                 {
565                         return GetDeleteCommand (false);
566                 }
567
568                 public DbCommand GetDeleteCommand (bool useColumnsForParameterNames)
569                 {
570                         BuildCache (true);
571                         if (_deleteCommand == null || useColumnsForParameterNames)
572                                 return CreateDeleteCommand (useColumnsForParameterNames);
573                         return _deleteCommand;
574                 }
575
576                 public DbCommand GetInsertCommand ()
577                 {
578                         return GetInsertCommand (false, null);
579                 }
580
581                 public DbCommand GetInsertCommand (bool useColumnsForParameterNames)
582                 {
583                         return GetInsertCommand (useColumnsForParameterNames, null);
584                 }
585
586                 internal DbCommand GetInsertCommand (bool useColumnsForParameterNames, DataRow row)
587                 {
588                         BuildCache (true);
589                         if (_insertCommand == null || useColumnsForParameterNames)
590                                 return CreateInsertCommand (useColumnsForParameterNames, row);
591                         return _insertCommand;
592                 }
593
594                 public DbCommand GetUpdateCommand ()
595                 {
596                         return GetUpdateCommand (false);
597                 }
598
599                 public DbCommand GetUpdateCommand (bool useColumnsForParameterNames)
600                 {
601                         BuildCache (true);
602                         if (_updateCommand == null || useColumnsForParameterNames)
603                                 return CreateUpdateCommand (useColumnsForParameterNames);
604                         return _updateCommand;
605                 }
606
607                 protected virtual DbCommand InitializeCommand (DbCommand command)
608                 {
609                         if (_dbCommand == null) {
610                                 _dbCommand = SourceCommand;
611                         } else {
612                                 _dbCommand.CommandTimeout = 30;
613                                 _dbCommand.Transaction = null;
614                                 _dbCommand.CommandType = CommandType.Text;
615                                 _dbCommand.UpdatedRowSource = UpdateRowSource.None;
616                         }
617                         return _dbCommand;
618
619                 }
620
621                 public virtual string QuoteIdentifier (string unquotedIdentifier)
622                 {
623                         throw new NotSupportedException ();
624                 }
625
626                 public virtual string UnquoteIdentifier (string quotedIdentifier)
627                 {
628                         if (quotedIdentifier == null) {
629                                 throw new ArgumentNullException ("Quoted identifier parameter cannot be null");
630                         }
631                         string unquotedIdentifier = quotedIdentifier.Trim ();
632                         if (unquotedIdentifier.StartsWith (this.QuotePrefix)) {
633                                 unquotedIdentifier = unquotedIdentifier.Remove (0, 1);
634                         }
635                         if (unquotedIdentifier.EndsWith (this.QuoteSuffix)) {
636                                 unquotedIdentifier = unquotedIdentifier.Remove (unquotedIdentifier.Length - 1, 1);
637                         }
638                         return unquotedIdentifier;
639                 }
640
641                 public virtual void RefreshSchema ()
642                 {
643                         _tableName = String.Empty;
644                         _dbSchemaTable = null;
645                         _deleteCommand = null;
646                         _updateCommand = null;
647                         _insertCommand = null;
648                 }
649
650                 protected void RowUpdatingHandler (RowUpdatingEventArgs rowUpdatingEvent)
651                 {
652                         if (rowUpdatingEvent.Command != null)
653                                 return;
654                         try {
655                                 switch (rowUpdatingEvent.StatementType) {
656                                 case StatementType.Insert:
657                                         rowUpdatingEvent.Command = GetInsertCommand (false, rowUpdatingEvent.Row);
658                                         break;
659                                 case StatementType.Update:
660                                         rowUpdatingEvent.Command = GetUpdateCommand ();
661                                         break;
662                                 case StatementType.Delete:
663                                         rowUpdatingEvent.Command = GetDeleteCommand ();
664                                         break;
665                                 }
666                         } catch (Exception e) {
667                                 rowUpdatingEvent.Errors = e;
668                                 rowUpdatingEvent.Status = UpdateStatus.ErrorsOccurred;
669                         }
670                 }
671
672                 protected abstract string GetParameterName (int parameterOrdinal);
673                 protected abstract string GetParameterName (String parameterName);
674                 protected abstract string GetParameterPlaceholder (int parameterOrdinal);
675
676                 protected abstract void SetRowUpdatingHandler (DbDataAdapter adapter);
677
678                 protected virtual DataTable GetSchemaTable (DbCommand sourceCommand)
679                 {
680                         using (DbDataReader rdr = sourceCommand.ExecuteReader ())
681                                 return rdr.GetSchemaTable ();
682                 }
683
684                 static void CheckEnumValue (Type type, int value)
685                 {
686                         if (Enum.IsDefined (type, value))
687                                 return;
688
689                         string typename = type.Name;
690                         string msg = string.Format (CultureInfo.CurrentCulture,
691                                 "Value {0} is not valid for {1}.", value,
692                                 typename);
693                         throw new ArgumentOutOfRangeException (typename, msg);
694                 }
695         }
696 }
697
698 #endif