svn path=/trunk/mcs/; revision=104772
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlCommandBuilder.cs
1 //
2 // System.Data.SqlClient.SqlCommandBuilder.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2002
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 using System;
34 using System.Collections;
35 using System.ComponentModel;
36 using System.Data;
37 using System.Data.Common;
38 using System.Text;
39
40 namespace System.Data.SqlClient
41 {
42 #if NET_2_0
43         public sealed class SqlCommandBuilder : DbCommandBuilder
44 #else
45         public sealed class SqlCommandBuilder : Component
46 #endif // NET_2_0
47         {
48                 #region Fields
49
50                 bool disposed;
51
52                 DataTable dbSchemaTable;
53                 SqlDataAdapter adapter;
54                 string quotePrefix;
55                 string quoteSuffix;
56                 string [] columnNames;
57                 string tableName;
58 #if NET_2_0
59                 string _catalogSeperator = ".";
60                 string _schemaSeperator = ".";
61                 CatalogLocation _catalogLocation = CatalogLocation.Start;
62 #endif
63         
64                 SqlCommand deleteCommand;
65                 SqlCommand insertCommand;
66                 SqlCommand updateCommand;
67
68                 // Used to construct WHERE clauses
69                 static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
70                 static readonly string clause2 = "({0} = {1})";
71
72                 private SqlRowUpdatingEventHandler rowUpdatingHandler;
73
74                 #endregion // Fields
75
76                 #region Constructors
77
78                 public SqlCommandBuilder ()
79                 {
80                         dbSchemaTable = null;
81                         adapter = null;
82 #if NET_2_0
83                         quoteSuffix = "]";
84                         quotePrefix = "[";
85 #else
86                         quoteSuffix = String.Empty;
87                         quotePrefix = String.Empty;
88 #endif
89                 }
90
91                 public SqlCommandBuilder (SqlDataAdapter adapter)
92                         : this ()
93                 {
94                         DataAdapter = adapter;
95                 }
96
97                 #endregion // Constructors
98
99                 #region Properties
100
101 #if !NET_2_0
102                 [DataSysDescription ("The DataAdapter for which to automatically generate SqlCommands")]
103 #endif
104                 [DefaultValue (null)]
105                 public new SqlDataAdapter DataAdapter {
106                         get { return adapter; }
107                         set { 
108                                 if (adapter != null)
109                                         adapter.RowUpdating -= new SqlRowUpdatingEventHandler (RowUpdatingHandler);
110
111                                 adapter = value; 
112                                 if (adapter != null)
113                                         adapter.RowUpdating += new SqlRowUpdatingEventHandler (RowUpdatingHandler);
114                         }
115                 }
116
117                 private string QuotedTableName {
118                         get { return GetQuotedString (tableName); }
119                 }
120
121                 [Browsable (false)]
122                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
123 #if !NET_2_0
124                 [DataSysDescription ("The character used in a text command as the opening quote for quoting identifiers that contain special characters.")]
125 #else
126                 [EditorBrowsable (EditorBrowsableState.Never)]
127 #endif // NET_2_0
128                 public
129 #if NET_2_0
130                 override
131 #endif // NET_2_0
132                 string QuotePrefix {
133                         get { return quotePrefix; }
134                         set {
135                                 if (dbSchemaTable != null)
136                                         throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
137                                 quotePrefix = value; 
138                         }
139                 }
140
141                 [Browsable (false)]
142                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
143 #if !NET_2_0
144                 [DataSysDescription ("The character used in a text command as the closing quote for quoting identifiers that contain special characters. ")]
145 #else
146                 [EditorBrowsable (EditorBrowsableState.Never)]
147 #endif // NET_2_0
148                 public
149 #if NET_2_0
150                 override
151 #endif // NET_2_0
152                 string QuoteSuffix {
153                         get { return quoteSuffix; }
154                         set {
155                                 if (dbSchemaTable != null)
156                                         throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
157                                 quoteSuffix = value; 
158                         }
159                 }
160
161 #if NET_2_0
162                 [EditorBrowsable (EditorBrowsableState.Never)]
163                 [Browsable (false)]
164                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
165 #if !NET_2_0
166                 [DefaultValue (".")]
167 #endif
168                 public override string CatalogSeparator {
169                         get { return _catalogSeperator; }
170                         set { if (value != null) _catalogSeperator = value; }
171                 }
172
173                 [EditorBrowsable (EditorBrowsableState.Never)]
174                 [Browsable (false)]
175                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
176 #if !NET_2_0
177                 [DefaultValue (".")]
178 #endif
179                 public override string SchemaSeparator {
180                         get { return _schemaSeperator; }
181                         set {  if (value != null) _schemaSeperator = value; }
182                 }
183
184                 [EditorBrowsable (EditorBrowsableState.Never)]
185                 [Browsable (false)]
186                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
187 #if !NET_2_0
188                 [DefaultValue (CatalogLocation.Start)]
189 #endif
190                 public override CatalogLocation CatalogLocation {
191                         get { return _catalogLocation; }
192                         set { _catalogLocation = value; }
193                 }
194
195 #endif // NET_2_0
196
197                 private SqlCommand SourceCommand {
198                         get {
199                                 if (adapter != null)
200                                         return adapter.SelectCommand;
201                                 return null;
202                         }
203                 }
204
205                 #endregion // Properties
206
207                 #region Methods
208
209                 private void BuildCache (bool closeConnection)
210                 {
211                         SqlCommand sourceCommand = SourceCommand;
212                         if (sourceCommand == null)
213                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
214                         SqlConnection connection = sourceCommand.Connection;
215                         if (connection == null)
216                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
217                                 
218                         if (dbSchemaTable == null) {
219                                 if (connection.State == ConnectionState.Open)
220                                         closeConnection = false;        
221                                 else
222                                         connection.Open ();
223         
224                                 SqlDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
225                                 dbSchemaTable = reader.GetSchemaTable ();
226                                 reader.Close ();
227                                 if (closeConnection)
228                                         connection.Close ();    
229                                 BuildInformation (dbSchemaTable);
230                         }
231                 }
232                 
233                 private void BuildInformation (DataTable schemaTable)
234                 {
235                         tableName = String.Empty;
236                         foreach (DataRow schemaRow in schemaTable.Rows) {
237                                 if (schemaRow.IsNull ("BaseTableName") ||
238                                     (string) schemaRow ["BaseTableName"] == String.Empty)
239                                         continue;
240
241                                 if (tableName == String.Empty) 
242                                         tableName = (string) schemaRow ["BaseTableName"];
243                                 else if (tableName != (string) schemaRow["BaseTableName"])
244                                         throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
245                         }
246                         if (tableName == String.Empty)
247                                 throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
248                         dbSchemaTable = schemaTable;
249                 }
250
251                 private SqlCommand CreateDeleteCommand (bool option)
252                 {
253                         // If no table was found, then we can't do an delete
254                         if (QuotedTableName == String.Empty)
255                                 return null;
256
257                         CreateNewCommand (ref deleteCommand);
258
259                         string command = String.Format ("DELETE FROM {0}", QuotedTableName);
260                         StringBuilder columns = new StringBuilder ();
261                         StringBuilder whereClause = new StringBuilder ();
262                         string dsColumnName = String.Empty;
263                         bool keyFound = false;
264                         int parmIndex = 1;
265
266                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
267                                 if ((bool)schemaRow["IsExpression"] == true)
268                                         continue;
269                                 if (!IncludedInWhereClause (schemaRow)) 
270                                         continue;
271
272                                 if (whereClause.Length > 0) 
273                                         whereClause.Append (" AND ");
274
275                                 bool isKey = (bool) schemaRow ["IsKey"];
276                                 SqlParameter parameter = null;
277
278                                 if (isKey)
279                                         keyFound = true;
280
281                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
282                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
283                                 //following the 2.0 approach
284                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
285                                 if (!isKey && allowNull) {
286                                         if (option) {
287                                                 parameter = deleteCommand.Parameters.Add (String.Format ("@{0}",
288                                                                                                          schemaRow ["BaseColumnName"]),
289                                                                                           SqlDbType.Int);
290                                         } else {
291                                                 parameter = deleteCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
292                                                                                           SqlDbType.Int);
293                                         }
294                                         String sourceColumnName = (string) schemaRow ["BaseColumnName"];
295                                         parameter.Value = 1;
296
297                                         whereClause.Append ("(");
298                                         whereClause.Append (String.Format (clause1, parameter.ParameterName, 
299                                                                         GetQuotedString (sourceColumnName)));
300                                         whereClause.Append (" OR ");
301                                 }
302
303                                 if (option) {
304                                         parameter = deleteCommand.Parameters.Add (CreateParameter (schemaRow));
305                                 } else {
306                                         parameter = deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
307                                 }
308                                 parameter.SourceVersion = DataRowVersion.Original;
309
310                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
311
312                                 if (!isKey && allowNull)
313                                         whereClause.Append (")");
314                         }
315                         if (!keyFound)
316                                 throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
317
318                         // We're all done, so bring it on home
319                         string sql = String.Format ("{0} WHERE ({1})", command, whereClause.ToString ());
320                         deleteCommand.CommandText = sql;
321                         return deleteCommand;
322                 }
323
324                 private SqlCommand CreateInsertCommand (bool option)
325                 {
326                         if (QuotedTableName == String.Empty)
327                                 return null;
328
329                         CreateNewCommand (ref insertCommand);
330
331                         string command = String.Format ("INSERT INTO {0}", QuotedTableName);
332                         string sql;
333                         StringBuilder columns = new StringBuilder ();
334                         StringBuilder values = new StringBuilder ();
335                         string dsColumnName = String.Empty;
336
337                         int parmIndex = 1;
338                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
339                                 if (!IncludedInInsert (schemaRow))
340                                         continue;
341
342                                 if (parmIndex > 1) {
343                                         columns.Append (", ");
344                                         values.Append (", ");
345                                 }
346
347                                 SqlParameter parameter = null;
348                                 if (option) {
349                                         parameter = insertCommand.Parameters.Add (CreateParameter (schemaRow));
350                                 } else {
351                                         parameter = insertCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
352                                 }
353                                 parameter.SourceVersion = DataRowVersion.Current;
354
355                                 columns.Append (GetQuotedString (parameter.SourceColumn));
356                                 values.Append (parameter.ParameterName);
357                         }
358
359                         sql = String.Format ("{0} ({1}) VALUES ({2})", command, columns.ToString (), values.ToString ());
360                         insertCommand.CommandText = sql;
361                         return insertCommand;
362                 }
363
364                 private void CreateNewCommand (ref SqlCommand command)
365                 {
366                         SqlCommand sourceCommand = SourceCommand;
367                         if (command == null) {
368                                 command = sourceCommand.Connection.CreateCommand ();
369                                 command.CommandTimeout = sourceCommand.CommandTimeout;
370                                 command.Transaction = sourceCommand.Transaction;
371                         }
372                         command.CommandType = CommandType.Text;
373                         command.UpdatedRowSource = UpdateRowSource.None;
374                         command.Parameters.Clear ();
375                 }
376
377                 private SqlCommand CreateUpdateCommand (bool option)
378                 {
379                         // If no table was found, then we can't do an update
380                         if (QuotedTableName == String.Empty)
381                                 return null;
382
383                         CreateNewCommand (ref updateCommand);
384
385                         string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
386                         StringBuilder columns = new StringBuilder ();
387                         StringBuilder whereClause = new StringBuilder ();
388                         int parmIndex = 1;
389                         string dsColumnName = String.Empty;
390                         bool keyFound = false;
391
392                         // First, create the X=Y list for UPDATE
393                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
394                                 if (!IncludedInUpdate (schemaRow))
395                                         continue;
396                                 if (columns.Length > 0) 
397                                         columns.Append (", ");
398
399                                 SqlParameter parameter = null;
400                                 if (option) {
401                                         parameter = updateCommand.Parameters.Add (CreateParameter (schemaRow));
402                                 } else {
403                                         parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
404                                 }
405                                 parameter.SourceVersion = DataRowVersion.Current;
406
407                                 columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
408                         }
409
410                         // Now, create the WHERE clause.  This may be optimizable, but it would be ugly to incorporate
411                         // into the loop above.  "Premature optimization is the root of all evil." -- Knuth
412                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
413                                 if ((bool)schemaRow["IsExpression"] == true)
414                                         continue;
415
416                                 if (!IncludedInWhereClause (schemaRow)) 
417                                         continue;
418
419                                 if (whereClause.Length > 0) 
420                                         whereClause.Append (" AND ");
421
422                                 bool isKey = (bool) schemaRow ["IsKey"];
423                                 SqlParameter parameter = null;
424
425
426                                 if (isKey)
427                                         keyFound = true;
428
429                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
430                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
431                                 //following the 2.0 approach
432                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
433                                 if (!isKey && allowNull) {
434                                         if (option) {
435                                                 parameter = updateCommand.Parameters.Add (String.Format ("@{0}",
436                                                                                                          schemaRow ["BaseColumnName"]),
437                                                                                           SqlDbType.Int);
438                                         } else {
439                                                 parameter = updateCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
440                                                                                           SqlDbType.Int);
441                                         }
442                                         parameter.Value = 1;
443                                         whereClause.Append ("(");
444                                         whereClause.Append (String.Format (clause1, parameter.ParameterName,
445                                                                         GetQuotedString ((string) schemaRow ["BaseColumnName"])));
446                                         whereClause.Append (" OR ");
447                                 }
448
449                                 if (option) {
450                                         parameter = updateCommand.Parameters.Add (CreateParameter (schemaRow));
451                                 } else {
452                                         parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
453                                 }
454                                 parameter.SourceVersion = DataRowVersion.Original;
455
456                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
457
458                                 if (!isKey && allowNull)
459                                         whereClause.Append (")");
460                         }
461                         if (!keyFound)
462                                 throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
463
464                         // We're all done, so bring it on home
465                         string sql = String.Format ("{0}{1} WHERE ({2})", command, columns.ToString (), whereClause.ToString ());
466                         updateCommand.CommandText = sql;
467                         return updateCommand;
468                 }
469
470                 private SqlParameter CreateParameter (DataRow schemaRow)
471                 {
472                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
473                         string name = String.Format ("@{0}", sourceColumn);
474                         SqlDbType sqlDbType = (SqlDbType) schemaRow ["ProviderType"];
475                         int size = (int) schemaRow ["ColumnSize"];
476
477                         return new SqlParameter (name, sqlDbType, size, sourceColumn);
478                 }
479
480                 private SqlParameter CreateParameter (int parmIndex, DataRow schemaRow)
481                 {
482                         string name = String.Format ("@p{0}", parmIndex);
483                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
484                         SqlDbType sqlDbType = (SqlDbType) schemaRow ["ProviderType"];
485                         int size = (int) schemaRow ["ColumnSize"];
486
487                         return new SqlParameter (name, sqlDbType, size, sourceColumn);
488                 }
489
490                 public static void DeriveParameters (SqlCommand command)
491                 {
492                         command.DeriveParameters ();
493                 }
494
495 #if NET_2_0
496                 new
497 #else
498                 protected override
499 #endif
500                 void Dispose (bool disposing)
501                 {
502                         if (!disposed) {
503                                 if (disposing) {
504                                         if (insertCommand != null)
505                                                 insertCommand.Dispose ();
506                                         if (deleteCommand != null)
507                                                 deleteCommand.Dispose ();
508                                         if (updateCommand != null)
509                                                 updateCommand.Dispose ();
510                                         if (dbSchemaTable != null)
511                                                 dbSchemaTable.Dispose ();
512                                 }
513                                 disposed = true;
514                         }
515                 }
516
517                 public 
518 #if NET_2_0
519                 new
520 #endif // NET_2_0
521                 SqlCommand GetDeleteCommand ()
522                 {
523                         BuildCache (true);
524                         if (deleteCommand == null)
525                                 return CreateDeleteCommand (false);
526                         return deleteCommand;
527                 }
528
529                 public
530 #if NET_2_0
531                 new
532 #endif // NET_2_0
533                 SqlCommand GetInsertCommand ()
534                 {
535                         BuildCache (true);
536                         if (insertCommand == null)
537                                 return CreateInsertCommand (false);
538                         return insertCommand;
539                 }
540
541                 private string GetQuotedString (string value)
542                 {
543                         if (value == String.Empty || value == null)
544                                 return value;
545                         if (quotePrefix == String.Empty && quoteSuffix == String.Empty)
546                                 return value;
547                         return String.Format ("{0}{1}{2}", quotePrefix, value, quoteSuffix);
548                 }
549
550                 public 
551 #if NET_2_0
552                 new
553 #endif // NET_2_0
554                 SqlCommand GetUpdateCommand ()
555                 {
556                         BuildCache (true);
557                         if (updateCommand == null)
558                                 return CreateUpdateCommand (false);
559                         return updateCommand;
560                 }
561
562 #if NET_2_0
563                 public new SqlCommand GetUpdateCommand (bool useColumnsForParameterNames)
564                 {
565                         BuildCache (true);
566                         if (updateCommand == null)
567                                 return CreateUpdateCommand (useColumnsForParameterNames);
568                         return updateCommand;
569                 }
570
571                 public new SqlCommand GetDeleteCommand (bool useColumnsForParameterNames)
572                 {
573                         BuildCache (true);
574                         if (deleteCommand == null)
575                                 return CreateDeleteCommand (useColumnsForParameterNames);
576                         return deleteCommand;
577                 }
578
579                 public new SqlCommand GetInsertCommand (bool useColumnsForParameterNames)
580                 {
581                         BuildCache (true);
582                         if (insertCommand == null)
583                                 return CreateInsertCommand (useColumnsForParameterNames);
584                         return insertCommand;
585                 }
586                 
587                 public override string QuoteIdentifier (string unquotedIdentifier)
588                 {
589                         return base.QuoteIdentifier (unquotedIdentifier);
590                 }
591                 
592                 public override string UnquoteIdentifier (string quotedIdentifier)
593                 {
594                         return base.UnquoteIdentifier (quotedIdentifier);
595                 }
596                 
597 #endif // NET_2_0
598
599                 private bool IncludedInInsert (DataRow schemaRow)
600                 {
601                         // If the parameter has one of these properties, then we don't include it in the insert:
602                         // AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
603
604                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
605                                 return false;
606                         if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
607                                 return false;
608                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
609                                 return false;
610                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
611                                 return false;
612                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
613                                 return false;
614                         return true;
615                 }
616
617                 private bool IncludedInUpdate (DataRow schemaRow)
618                 {
619                         // If the parameter has one of these properties, then we don't include it in the insert:
620                         // AutoIncrement, Hidden, RowVersion
621
622                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
623                                 return false;
624                         if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
625                                 return false;
626                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
627                                 return false;
628                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
629                                 return false;
630                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
631                                 return false;
632
633                         return true;
634                 }
635
636                 private bool IncludedInWhereClause (DataRow schemaRow)
637                 {
638                         if ((bool) schemaRow ["IsLong"])
639                                 return false;
640                         return true;
641                 }
642
643 #if NET_2_0
644                 new
645 #else
646                 public
647 #endif
648                 void RefreshSchema () 
649                 {
650                         // FIXME: "Figure out what else needs to be cleaned up when we refresh."
651                         tableName = String.Empty;
652                         dbSchemaTable = null;
653                         CreateNewCommand (ref deleteCommand);
654                         CreateNewCommand (ref updateCommand);
655                         CreateNewCommand (ref insertCommand);
656                 }
657
658 #if NET_2_0
659                 protected override void ApplyParameterInfo (DbParameter parameter,
660                                                             DataRow datarow,
661                                                             StatementType statementType,
662                                                             bool whereClause)
663                 {
664                         SqlParameter sqlParam = (SqlParameter) parameter;
665                         sqlParam.Size = int.Parse (datarow ["ColumnSize"].ToString ());
666                         if (datarow ["NumericPrecision"] != DBNull.Value) {
667                                 sqlParam.Precision = byte.Parse (datarow ["NumericPrecision"].ToString ());
668                         }
669                         if (datarow ["NumericScale"] != DBNull.Value) {
670                                 sqlParam.Scale = byte.Parse (datarow ["NumericScale"].ToString ());
671                         }
672                         sqlParam.SqlDbType = (SqlDbType) datarow ["ProviderType"];
673                 }
674
675                 protected override string GetParameterName (int parameterOrdinal)
676                 {
677                         return String.Format ("@p{0}",  parameterOrdinal);
678                 }
679
680                 protected override string GetParameterName (string parameterName)
681                 {
682                         return String.Format ("@{0}", parameterName);
683                 }
684
685                 protected override string GetParameterPlaceholder (int parameterOrdinal)
686                 {
687                         return GetParameterName (parameterOrdinal);
688                 }
689 #endif // NET_2_0
690
691                 #endregion // Methods
692
693                 #region Event Handlers
694
695                 private void RowUpdatingHandler (object sender, SqlRowUpdatingEventArgs args)
696                 {
697                         if (args.Command != null)
698                                 return;
699                         try {
700                                 switch (args.StatementType) {
701                                 case StatementType.Insert:
702                                         args.Command = GetInsertCommand ();
703                                         break;
704                                 case StatementType.Update:
705                                         args.Command = GetUpdateCommand ();
706                                         break;
707                                 case StatementType.Delete:
708                                         args.Command = GetDeleteCommand ();
709                                         break;
710                                 }
711                         } catch (Exception e) {
712                                 args.Errors = e;
713                                 args.Status = UpdateStatus.ErrorsOccurred;
714                         }
715                 }
716
717 #if NET_2_0
718                 protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
719                 {
720                         if (!(adapter is SqlDataAdapter)) {
721                                 throw new InvalidOperationException ("Adapter needs to be a SqlDataAdapter");
722                         }
723                         rowUpdatingHandler = new SqlRowUpdatingEventHandler (RowUpdatingHandler);
724                         ((SqlDataAdapter) adapter).RowUpdating += rowUpdatingHandler;
725                 }
726
727                 protected override DataTable GetSchemaTable (DbCommand srcCommand)
728                 {
729                         using (SqlDataReader rdr = (SqlDataReader) srcCommand.ExecuteReader ())
730                                 return rdr.GetSchemaTable ();
731                 }
732
733                 protected override DbCommand InitializeCommand (DbCommand command)
734                 {
735                         if (command == null) {
736                                 command = new SqlCommand ();
737                         } else {
738                                 command.CommandTimeout = 30;
739                                 command.Transaction = null;
740                                 command.CommandType = CommandType.Text;
741                                 command.UpdatedRowSource = UpdateRowSource.None;
742                         }
743                         return command;
744                 }
745 #endif // NET_2_0
746
747                 #endregion // Event Handlers
748         }
749 }