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