Mark tests as not working under TARGET_JVM
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlCommand.cs
1 //
2 // System.Data.SqlClient.SqlCommand.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Diego Caravana (diego@toth.it)
9 //
10 // (C) Ximian, Inc 2002 http://www.ximian.com/
11 // (C) Daniel Morgan, 2002
12 // Copyright (C) Tim Coleman, 2002
13 //
14
15 //
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
25 // 
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
28 // 
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 //
37
38 using Mono.Data.Tds;
39 using Mono.Data.Tds.Protocol;
40 using System;
41 using System.Collections;
42 using System.Collections.Specialized;
43 using System.ComponentModel;
44 using System.Data;
45 using System.Data.Common;
46 using System.Runtime.InteropServices;
47 using System.Text;
48 using System.Xml;
49
50 namespace System.Data.SqlClient {
51         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
52          [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
53         [DefaultEventAttribute ("RecordsAffected")]
54 #if NET_2_0
55         public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
56 #else
57         public sealed class SqlCommand : Component, IDbCommand, ICloneable
58 #endif // NET_2_0
59         {
60                 #region Fields
61
62                 //bool disposed = false;
63                 int commandTimeout;
64                 bool designTimeVisible;
65                 string commandText;
66                 CommandType commandType;
67                 SqlConnection connection;
68                 SqlTransaction transaction;
69                 UpdateRowSource updatedRowSource;
70                 CommandBehavior behavior = CommandBehavior.Default;
71                 SqlParameterCollection parameters;
72                 string preparedStatement = null;
73
74                 #endregion // Fields
75
76                 #region Constructors
77
78                 public SqlCommand() 
79                         : this (String.Empty, null, null)
80                 {
81                 }
82
83                 public SqlCommand (string commandText) 
84                         : this (commandText, null, null)
85                 {
86                 }
87
88                 public SqlCommand (string commandText, SqlConnection connection) 
89                         : this (commandText, connection, null)
90                 {
91                 }
92
93                 public SqlCommand (string commandText, SqlConnection connection, SqlTransaction transaction) 
94                 {
95                         this.commandText = commandText;
96                         this.connection = connection;
97                         this.transaction = transaction;
98                         this.commandType = CommandType.Text;
99                         this.updatedRowSource = UpdateRowSource.Both;
100
101                         this.designTimeVisible = false;
102                         this.commandTimeout = 30;
103                         parameters = new SqlParameterCollection (this);
104                 }
105
106                 private SqlCommand(string commandText, SqlConnection connection, SqlTransaction transaction, CommandType commandType, UpdateRowSource updatedRowSource, bool designTimeVisible, int commandTimeout, SqlParameterCollection parameters)
107                 {
108                         this.commandText = commandText;
109                         this.connection = connection;
110                         this.transaction = transaction;
111                         this.commandType = commandType;
112                         this.updatedRowSource = updatedRowSource;
113                         this.designTimeVisible = designTimeVisible;
114                         this.commandTimeout = commandTimeout;
115                         this.parameters = new SqlParameterCollection(this);
116                         for (int i = 0;i < parameters.Count;i++)
117                               this.parameters.Add(((ICloneable)parameters[i]).Clone()); 
118                 }
119                 #endregion // Constructors
120
121                 #region Properties
122
123                 internal CommandBehavior CommandBehavior {
124                         get { return behavior; }
125                 }
126
127                 [DataCategory ("Data")]
128 #if !NET_2_0
129                 [DataSysDescription ("Command text to execute.")]
130 #endif
131                 [DefaultValue ("")]
132                 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
133                 [RefreshProperties (RefreshProperties.All)]
134                 public 
135 #if NET_2_0
136                 override 
137 #endif //NET_2_0
138                 string CommandText {
139                         get { return commandText; }
140                         set { 
141                                 if (value != commandText && preparedStatement != null)
142                                         Unprepare ();
143                                 commandText = value; 
144                         }
145                 }
146
147 #if !NET_2_0
148                 [DataSysDescription ("Time to wait for command to execute.")]
149                 [DefaultValue (30)]
150 #endif
151                 public 
152 #if NET_2_0
153                 override
154 #endif //NET_2_0
155                 int CommandTimeout {
156                         get { return commandTimeout;  }
157                         set { 
158                                 if (value < 0)
159                                         throw new ArgumentException ("The property value assigned is less than 0.");
160                                 commandTimeout = value; 
161                         }
162                 }
163
164                 [DataCategory ("Data")]
165 #if !NET_2_0
166                 [DataSysDescription ("How to interpret the CommandText.")]
167 #endif
168                 [DefaultValue (CommandType.Text)]
169                 [RefreshProperties (RefreshProperties.All)]
170                 public 
171 #if NET_2_0
172                 override 
173 #endif //NET_2_0
174                 CommandType CommandType {
175                         get { return commandType; }
176                         set { 
177                                 if (value == CommandType.TableDirect)
178 #if NET_2_0
179                                         throw new ArgumentOutOfRangeException ("CommandType.TableDirect is not supported " +
180                                                                                "by the Mono SqlClient Data Provider.");
181 #else
182                                         throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SqlClient Data Provider.");
183 #endif
184
185                                 if (!Enum.IsDefined (typeof (CommandType), value))
186 #if NET_2_0
187                                         throw new ArgumentOutOfRangeException (String.Format ("The CommandType enumeration value, {0}, is invalid",
188                                                                                               value));
189 #else
190                                         throw ExceptionHelper.InvalidEnumValueException ("CommandType", value);
191 #endif
192                                 commandType = value; 
193                         }
194                 }
195
196                 [DataCategory ("Behavior")]
197                 [DefaultValue (null)]
198 #if !NET_2_0
199                 [DataSysDescription ("Connection used by the command.")]
200 #endif
201                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]                
202                 public 
203 #if NET_2_0
204                 new
205 #endif //NET_2_0
206                 SqlConnection Connection {
207                         get { return connection; }
208                         set {
209                                 if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
210                                         throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
211                                 transaction = null;
212                                 connection = value; 
213                         }
214                 }
215
216                 [Browsable (false)]
217                 [DefaultValue (true)]
218                 [DesignOnly (true)]
219                 [EditorBrowsable (EditorBrowsableState.Never)]
220                 public 
221 #if NET_2_0
222                 override
223 #endif //NET_2_0
224                 bool DesignTimeVisible {
225                         get { return designTimeVisible; } 
226                         set { designTimeVisible = value; }
227                 }
228
229                 [DataCategory ("Data")]
230 #if !NET_2_0
231                 [DataSysDescription ("The parameters collection.")]
232 #endif
233                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
234                 public 
235 #if NET_2_0
236                 new 
237 #endif //NET_2_0
238                 SqlParameterCollection Parameters {
239                         get { return parameters; }
240                 }
241
242                 internal ITds Tds {
243                         get { return Connection.Tds; }
244                 }
245
246                 IDbConnection IDbCommand.Connection {
247                         get { return Connection; }
248                         set { 
249                                 if (!(value is SqlConnection))
250                                         throw new InvalidCastException ("The value was not a valid SqlConnection.");
251                                 Connection = (SqlConnection) value;
252                         }
253                 }
254
255                 IDataParameterCollection IDbCommand.Parameters  {
256                         get { return Parameters; }
257                 }
258
259                 IDbTransaction IDbCommand.Transaction {
260                         get { return Transaction; }
261                         set { 
262                                 if (!(value is SqlTransaction))
263                                         throw new ArgumentException ();
264                                 Transaction = (SqlTransaction) value; 
265                         }
266                 }
267
268                 [Browsable (false)]
269 #if !NET_2_0
270                 [DataSysDescription ("The transaction used by the command.")]
271 #endif
272                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
273                 public new SqlTransaction Transaction {
274                         get { return transaction; }
275                         set { transaction = value; }
276                 }       
277
278                 [DataCategory ("Behavior")]
279 #if !NET_2_0
280                 [DataSysDescription ("When used by a DataAdapter.Update, how command results are applied to the current DataRow.")]
281 #endif
282                 [DefaultValue (UpdateRowSource.Both)]
283                 public 
284 #if NET_2_0
285                 override
286 #endif // NET_2_0
287                 UpdateRowSource UpdatedRowSource        {
288                         get { return updatedRowSource; }
289                         set { 
290                                 if (!Enum.IsDefined (typeof (UpdateRowSource), value))
291 #if NET_2_0
292                                         throw new ArgumentOutOfRangeException (String.Format ("The UpdateRowSource enumeration value, {0}, is invalid",
293                                                                                               value));
294 #else
295                                         throw ExceptionHelper.InvalidEnumValueException ("UpdateRowSource", value);
296 #endif
297                                 updatedRowSource = value;
298                         }
299                 }
300
301                 #endregion // Fields
302
303                 #region Methods
304
305                 public 
306 #if NET_2_0
307                 override
308 #endif // NET_2_0
309                 void Cancel () 
310                 {
311                         if (Connection == null || Connection.Tds == null)
312                                 return;
313                         Connection.Tds.Cancel ();
314                 }
315
316                 internal void CloseDataReader (bool moreResults)
317                 {
318                         Connection.DataReader = null;
319
320                         if ((behavior & CommandBehavior.CloseConnection) != 0)
321                                 Connection.Close ();
322
323                         // Reset the behavior
324                         behavior = CommandBehavior.Default;
325                         if (Tds != null)
326                                 Tds.SequentialAccess = false;
327                 }
328
329                 public new SqlParameter CreateParameter () 
330                 {
331                         return new SqlParameter ();
332                 }
333
334                 internal void DeriveParameters ()
335                 {
336                         if (commandType != CommandType.StoredProcedure)
337                                 throw new InvalidOperationException (String.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
338                         ValidateCommand ("DeriveParameters");
339
340                         SqlParameterCollection localParameters = new SqlParameterCollection (this);
341                         localParameters.Add ("@procedure_name", SqlDbType.NVarChar, commandText.Length).Value = commandText;
342
343                         string sql = "sp_procedure_params_rowset";
344
345                         Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
346
347                         SqlDataReader reader = new SqlDataReader (this);
348                         parameters.Clear ();
349                         object[] dbValues = new object[reader.FieldCount];
350
351                         while (reader.Read ()) {
352                                 reader.GetValues (dbValues);
353                                 parameters.Add (new SqlParameter (dbValues));
354                         }
355                         reader.Close ();        
356
357                 }
358
359                 private void Execute (CommandBehavior behavior, bool wantResults)
360                 {
361                         int index = 0;
362                         Connection.Tds.RecordsAffected = -1;
363                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
364                         foreach (TdsMetaParameter param in parms) {
365                                 bool flag = false;
366                                 string type = "";
367                                 if (param.Size == 0) {
368                                         switch (param.TypeName) {
369                                         case "nvarchar":
370                                                 type = "String";
371                                                 flag = true;
372                                                 break;
373                                         case "varbinary":
374                                                 type = "Byte[]";
375                                                 flag = true;
376                                                 break;
377                                         }
378                                 }
379                                 if (flag == true)
380                                         throw new InvalidOperationException (String.Format ("{0}[{1}]: the Size property should " +
381                                                                                             "not be of size 0",
382                                                                                             type,
383                                                                                             index));
384                                 index++;
385                         }
386                         if (preparedStatement == null) {
387                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
388                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
389
390                                 StringBuilder sql1 = new StringBuilder ();
391                                 StringBuilder sql2 = new StringBuilder ();
392
393                                 if (schemaOnly || keyInfo)
394                                         sql1.Append ("SET FMTONLY OFF;");
395                                 if (keyInfo) {
396                                         sql1.Append ("SET NO_BROWSETABLE ON;");
397                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
398                                 }
399                                 if (schemaOnly) {
400                                         sql1.Append ("SET FMTONLY ON;");
401                                         sql2.Append ("SET FMTONLY OFF;");
402                                 }
403
404                                 switch (CommandType) {
405                                 case CommandType.StoredProcedure:
406                                         if (keyInfo || schemaOnly)
407                                                 Connection.Tds.Execute (sql1.ToString ());
408                                         Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
409                                         if (keyInfo || schemaOnly)
410                                                 Connection.Tds.Execute (sql2.ToString ());
411                                         break;
412                                 case CommandType.Text:
413                                         string sql;
414                                         if (sql2.Length > 0) {
415                                                 sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
416                                         } else {
417                                                 sql = String.Format ("{0}{1}", sql1.ToString (), CommandText);
418                                         }
419                                         Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
420                                         break;
421                                 }
422                         }
423                         else 
424                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
425                 }
426
427                 public 
428 #if NET_2_0
429                 override
430 #endif // NET_2_0
431                 int ExecuteNonQuery ()
432                 {
433                         ValidateCommand ("ExecuteNonQuery");
434                         int result = 0;
435                         behavior = CommandBehavior.Default;
436
437                         try {
438                                 Execute (CommandBehavior.Default, false);
439                                 result = Connection.Tds.RecordsAffected;
440                         }
441                         catch (TdsTimeoutException e) {
442                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
443                         }
444
445                         GetOutputParameters ();
446                         return result;
447                 }
448
449                 public new SqlDataReader ExecuteReader ()
450                 {
451                         return ExecuteReader (CommandBehavior.Default);
452                 }
453
454                 public new SqlDataReader ExecuteReader (CommandBehavior behavior)
455                 {
456                         ValidateCommand ("ExecuteReader");
457                         try {
458                                 this.behavior = behavior;
459                                 if ((behavior & CommandBehavior.SequentialAccess) != 0)
460                                         Tds.SequentialAccess = true;
461                                 Execute (behavior, true);
462                                 Connection.DataReader = new SqlDataReader (this);
463                         }
464                         catch (TdsTimeoutException e) {
465                                 // if behavior is closeconnection, even if it throws exception
466                                 // the connection has to be closed.
467                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
468                                         Connection.Close ();
469                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
470                         } catch (SqlException) {
471                                 // if behavior is closeconnection, even if it throws exception
472                                 // the connection has to be closed.
473                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
474                                         Connection.Close ();
475
476                                 throw;
477                         }
478
479                         return Connection.DataReader;
480                 }
481
482                 public 
483 #if NET_2_0
484                 override
485 #endif // NET_2_0
486                 object ExecuteScalar ()
487                 {
488                         try {
489                                 object result = null;
490                                 ValidateCommand ("ExecuteScalar");
491                                 behavior = CommandBehavior.Default;
492                                 try {
493                                         Execute (CommandBehavior.Default, true);
494                                 }
495                                 catch (TdsTimeoutException e) {
496                                         throw SqlException.FromTdsInternalException ((TdsInternalException) e);
497                                 }
498
499                                 if (Connection.Tds.NextResult () && Connection.Tds.NextRow ())
500                                         result = Connection.Tds.ColumnValues[0];
501                                         
502                                 if (commandType == CommandType.StoredProcedure) {
503                                         Connection.Tds.SkipToEnd ();
504                                         GetOutputParameters ();
505                                 }
506
507                                 return result;
508                         }
509                         finally {
510                                 CloseDataReader (true);
511                         }
512                 }
513
514                 public XmlReader ExecuteXmlReader ()
515                 {
516                         ValidateCommand ("ExecuteXmlReader");
517                         behavior = CommandBehavior.Default;
518                         try { 
519                                 Execute (CommandBehavior.Default, true);
520                         }
521                         catch (TdsTimeoutException e) {
522                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
523                         }
524
525                         SqlDataReader dataReader = new SqlDataReader (this);
526                         SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
527                         XmlReader xmlReader = new XmlTextReader (textReader);
528                         return xmlReader;
529                 }
530
531                 internal void GetOutputParameters ()
532                 {
533                         IList list = Connection.Tds.OutputParameters;
534
535                         if (list != null && list.Count > 0) {
536
537                                 int index = 0;
538                                 foreach (SqlParameter parameter in parameters) {
539                                         if (parameter.Direction != ParameterDirection.Input) {
540                                                 parameter.Value = list [index];
541                                                 index += 1;
542                                         }
543                                         if (index >= list.Count)
544                                                 break;
545                                 }
546                         }
547                 }
548
549                 object ICloneable.Clone ()
550                 {
551                         return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout,  parameters);
552
553                 }
554
555                 IDbDataParameter IDbCommand.CreateParameter ()
556                 {
557                         return CreateParameter ();
558                 }
559
560                 IDataReader IDbCommand.ExecuteReader ()
561                 {
562                         return ExecuteReader ();
563                 }
564
565                 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
566                 {
567                         return ExecuteReader (behavior);
568                 }
569
570                 public 
571 #if NET_2_0
572                 override
573 #endif // NET_2_0
574                 void Prepare ()
575                 {
576                         ValidateCommand ("Prepare");
577
578                         if (CommandType == CommandType.StoredProcedure)
579                                 return;
580
581                         try {
582                                 foreach (SqlParameter param in Parameters)
583                                         param.CheckIfInitialized ();
584                         }catch (Exception e) {
585                                 throw new InvalidOperationException ("SqlCommand.Prepare requires " + e.Message);
586                         }
587
588                         preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
589                 }
590
591                 public void ResetCommandTimeout ()
592                 {
593                         commandTimeout = 30;
594                 }
595
596                 private void Unprepare ()
597                 {
598                         Connection.Tds.Unprepare (preparedStatement);
599                         preparedStatement = null;
600                 }
601
602                 private void ValidateCommand (string method)
603                 {
604                         if (Connection == null)
605 #if NET_2_0
606                                 throw new NullReferenceException (String.Format ("{0} requires a Connection object to continue.", method));
607 #else
608                                 throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
609 #endif
610                         if (Connection.Transaction != null && transaction != Connection.Transaction)
611                                 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
612                         if (Connection.State != ConnectionState.Open)
613 #if NET_2_0
614                                 throw new NullReferenceException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
615 #else
616                                 throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
617 #endif
618                         if (commandText == String.Empty || commandText == null)
619                                 throw new InvalidOperationException ("The command text for this Command has not been set.");
620                         if (Connection.DataReader != null)
621                                 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
622                         if (Connection.XmlReader != null)
623                                 throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
624 #if NET_2_0
625                         if (method.StartsWith ("Begin") && !Connection.AsyncProcessing)
626                                 throw new InvalidOperationException ("This Connection object is not " + 
627                                                                      "in Asynchronous mode. Use 'Asynchronous" +
628                                                                      " Processing = true' to set it.");
629 #endif // NET_2_0
630                 }
631
632 #if NET_2_0
633                 protected override DbParameter CreateDbParameter ()
634                 {
635                         return (DbParameter) CreateParameter ();
636                 }
637
638                 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
639                 {
640                         return (DbDataReader) ExecuteReader (behavior);
641                 }
642
643                 protected override DbConnection DbConnection 
644                 {
645                         get { return (DbConnection) Connection;  }
646                         set { Connection = (SqlConnection) value; }
647                 }
648                 
649                 protected override DbParameterCollection DbParameterCollection
650                 {
651                         get { return (DbParameterCollection) Parameters; }
652                 }
653
654                 protected override DbTransaction DbTransaction 
655                 {
656                         get { return (DbTransaction) Transaction; }
657                         set { Transaction = (SqlTransaction) value; }
658                 }
659 #endif // NET_2_0
660
661                 #endregion // Methods
662
663 #if NET_2_0
664                 #region Asynchronous Methods
665
666                 internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior, 
667                                                             bool wantResults,
668                                                             AsyncCallback callback, 
669                                                             object state)
670                 {
671                         IAsyncResult ar = null;
672                         Connection.Tds.RecordsAffected = -1;
673                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
674                         if (preparedStatement == null) {
675                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
676                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
677
678                                 StringBuilder sql1 = new StringBuilder ();
679                                 StringBuilder sql2 = new StringBuilder ();
680
681                                 if (schemaOnly || keyInfo)
682                                         sql1.Append ("SET FMTONLY OFF;");
683                                 if (keyInfo) {
684                                         sql1.Append ("SET NO_BROWSETABLE ON;");
685                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
686                                 }
687                                 if (schemaOnly) {
688                                         sql1.Append ("SET FMTONLY ON;");
689                                         sql2.Append ("SET FMTONLY OFF;");
690                                 }
691
692                                 switch (CommandType) {
693                                 case CommandType.StoredProcedure:
694                                         string prolog = "";
695                                         string epilog = "";
696                                         if (keyInfo || schemaOnly)
697                                                 prolog = sql1.ToString ();
698                                         if (keyInfo || schemaOnly)
699                                                 epilog = sql2.ToString ();
700                                         Connection.Tds.BeginExecuteProcedure (prolog,
701                                                                               epilog,
702                                                                               CommandText,
703                                                                               !wantResults,
704                                                                               parms,
705                                                                               callback,
706                                                                               state);
707                                                                               
708                                         break;
709                                 case CommandType.Text:
710                                         string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
711                                         if (wantResults)
712                                                 ar = Connection.Tds.BeginExecuteQuery (sql, parms, 
713                                                                                        callback, state);
714                                         else
715                                                 ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state);
716                                         break;
717                                 }
718                         }
719                         else 
720                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
721
722                         return ar;
723
724                 }
725
726                 internal void EndExecuteInternal (IAsyncResult ar)
727                 {
728                         SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar);
729                         Connection.Tds.WaitFor (sqlResult.InternalResult);
730                         Connection.Tds.CheckAndThrowException (sqlResult.InternalResult);
731                 }
732
733                 public IAsyncResult BeginExecuteNonQuery ()
734                 {
735                         return BeginExecuteNonQuery (null, null);
736                 }
737
738                 public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object state)
739                 {
740                         ValidateCommand ("BeginExecuteNonQuery");
741                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
742                         ar.EndMethod = "EndExecuteNonQuery";
743                         ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar);
744                         return ar;
745                 }
746
747                 public int EndExecuteNonQuery (IAsyncResult ar)
748                 {
749                         ValidateAsyncResult (ar, "EndExecuteNonQuery");
750                         EndExecuteInternal (ar);
751                         
752                         int ret = Connection.Tds.RecordsAffected;
753
754                         GetOutputParameters ();
755                         ( (SqlAsyncResult) ar).Ended = true;
756                         return ret;
757                 }
758
759                 public IAsyncResult BeginExecuteReader ()
760                 {
761                         return BeginExecuteReader (null, null, CommandBehavior.Default);
762                 }
763
764                 public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
765                 {
766                         return BeginExecuteReader (null, null, behavior);
767                 }
768                 
769                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state)
770                 {
771                         return BeginExecuteReader (callback, state, CommandBehavior.Default);
772                 }
773
774                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state, CommandBehavior behavior)
775                 {
776                         ValidateCommand ("BeginExecuteReader");
777                         this.behavior = behavior;
778                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
779                         ar.EndMethod = "EndExecuteReader";
780                         IAsyncResult tdsResult = BeginExecuteInternal (behavior, true, 
781                                                                        ar.BubbleCallback, state);
782                         ar.InternalResult = tdsResult;
783                         return ar;
784                 }
785
786                 public SqlDataReader EndExecuteReader (IAsyncResult ar)
787                 {
788                         ValidateAsyncResult (ar, "EndExecuteReader");
789                         EndExecuteInternal (ar);
790                         SqlDataReader reader = null;
791                         try {
792                                 reader = new SqlDataReader (this);
793                         }
794                         catch (TdsTimeoutException e) {
795                                 // if behavior is closeconnection, even if it throws exception
796                                 // the connection has to be closed.
797                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
798                                         Connection.Close ();
799                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
800                         } catch (SqlException) {
801                                 // if behavior is closeconnection, even if it throws exception
802                                 // the connection has to be closed.
803                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
804                                         Connection.Close ();
805
806                                 throw;
807                         }
808
809                         ( (SqlAsyncResult) ar).Ended = true;
810                         return reader;
811                 }
812
813                 public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object state)
814                 {
815                         ValidateCommand ("BeginExecuteXmlReader");
816                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
817                         ar.EndMethod = "EndExecuteXmlReader";
818                         ar.InternalResult = BeginExecuteInternal (behavior, true, 
819                                                                        ar.BubbleCallback, state);
820                         return ar;
821                 }
822
823                 public XmlReader EndExecuteXmlReader (IAsyncResult ar)
824                 {
825                         ValidateAsyncResult (ar, "EndExecuteXmlReader");
826                         EndExecuteInternal (ar);
827                         SqlDataReader reader = new SqlDataReader (this);
828                         SqlXmlTextReader textReader = new SqlXmlTextReader (reader);
829                         XmlReader xmlReader = new XmlTextReader (textReader);
830                         ( (SqlAsyncResult) ar).Ended = true;
831                         return xmlReader;
832                 }
833
834
835                 internal void ValidateAsyncResult (IAsyncResult ar, string endMethod)
836                 {
837                         if (ar == null)
838                                 throw new ArgumentException ("result passed is null!");
839                         if (! (ar is SqlAsyncResult))
840                                 throw new ArgumentException (String.Format ("cannot test validity of types {0}",
841                                                                             ar.GetType ()
842                                                                             ));
843                         SqlAsyncResult result = (SqlAsyncResult) ar;
844                         
845                         if (result.EndMethod != endMethod)
846                                 throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " + 
847                                                                                     "Expected call to {1} but {0} is called instead.",
848                                                                                     endMethod,
849                                                                                     result.EndMethod
850                                                                                     ));
851                         if (result.Ended)
852                                 throw new InvalidOperationException (String.Format ("The method {0}  cannot be called " + 
853                                                                                     "more than once for the same AsyncResult.",
854                                                                                     endMethod));
855
856                 }
857
858                 #endregion // Asynchronous Methods
859                 public event StatementCompletedEventHandler StatementCompleted;
860 #endif // NET_2_0
861         }
862 }