2 // System.Data.SqlClient.SqlCommand.cs
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)
10 // (C) Ximian, Inc 2002 http://www.ximian.com/
11 // (C) Daniel Morgan, 2002
12 // Copyright (C) Tim Coleman, 2002
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
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.
39 using Mono
.Data
.Tds
.Protocol
;
41 using System
.Collections
;
42 using System
.Collections
.Specialized
;
43 using System
.ComponentModel
;
45 using System
.Data
.Common
;
46 using System
.Runtime
.InteropServices
;
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 public sealed class SqlCommand
: Component
, IDbCommand
, ICloneable
57 bool disposed
= false;
59 bool designTimeVisible
;
61 CommandType commandType
;
62 SqlConnection connection
;
63 SqlTransaction transaction
;
64 UpdateRowSource updatedRowSource
;
65 CommandBehavior behavior
= CommandBehavior
.Default
;
66 SqlParameterCollection parameters
;
67 string preparedStatement
= null;
74 : this (String
.Empty
, null, null)
78 public SqlCommand (string commandText
)
79 : this (commandText
, null, null)
81 commandText
= commandText
;
84 public SqlCommand (string commandText
, SqlConnection connection
)
85 : this (commandText
, connection
, null)
87 Connection
= connection
;
90 public SqlCommand (string commandText
, SqlConnection connection
, SqlTransaction transaction
)
92 this.commandText
= commandText
;
93 this.connection
= connection
;
94 this.transaction
= transaction
;
95 this.commandType
= CommandType
.Text
;
96 this.updatedRowSource
= UpdateRowSource
.Both
;
98 this.designTimeVisible
= false;
99 this.commandTimeout
= 30;
100 parameters
= new SqlParameterCollection (this);
103 private SqlCommand(string commandText
, SqlConnection connection
, SqlTransaction transaction
, CommandType commandType
, UpdateRowSource updatedRowSource
, bool designTimeVisible
, int commandTimeout
, SqlParameterCollection parameters
)
105 this.commandText
= commandText
;
106 this.connection
= connection
;
107 this.transaction
= transaction
;
108 this.commandType
= commandType
;
109 this.updatedRowSource
= updatedRowSource
;
110 this.designTimeVisible
= designTimeVisible
;
111 this.commandTimeout
= commandTimeout
;
112 this.parameters
= new SqlParameterCollection(this);
113 for (int i
= 0;i
< parameters
.Count
;i
++)
114 this.parameters
.Add(((ICloneable
)parameters
[i
]).Clone());
116 #endregion // Constructors
120 internal CommandBehavior CommandBehavior
{
121 get { return behavior; }
124 [DataCategory ("Data")]
125 [DataSysDescription ("Command text to execute.")]
127 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)]
128 [RefreshProperties (RefreshProperties
.All
)]
129 public string CommandText
{
130 get { return commandText; }
132 if (value != commandText
&& preparedStatement
!= null)
138 [DataSysDescription ("Time to wait for command to execute.")]
140 public int CommandTimeout
{
141 get { return commandTimeout; }
143 if (commandTimeout
< 0)
144 throw new ArgumentException ("The property value assigned is less than 0.");
145 commandTimeout
= value;
149 [DataCategory ("Data")]
150 [DataSysDescription ("How to interpret the CommandText.")]
151 [DefaultValue (CommandType
.Text
)]
152 [RefreshProperties (RefreshProperties
.All
)]
153 public CommandType CommandType
{
154 get { return commandType; }
156 if (value == CommandType
.TableDirect
)
157 throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SqlClient Data Provider.");
162 [DataCategory ("Behavior")]
163 [DefaultValue (null)]
164 [DataSysDescription ("Connection used by the command.")]
165 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts
.AssemblyMicrosoft_VSDesigner
, "System.Drawing.Design.UITypeEditor, "+ Consts
.AssemblySystem_Drawing
)] public SqlConnection Connection
{
166 get { return connection; }
168 if (transaction
!= null && connection
.Transaction
!= null && connection
.Transaction
.IsOpen
)
169 throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
176 [DefaultValue (true)]
178 public bool DesignTimeVisible
{
179 get { return designTimeVisible; }
180 set { designTimeVisible = value; }
183 [DataCategory ("Data")]
184 [DataSysDescription ("The parameters collection.")]
185 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Content
)]
186 public SqlParameterCollection Parameters
{
187 get { return parameters; }
191 get { return Connection.Tds; }
194 IDbConnection IDbCommand
.Connection
{
195 get { return Connection; }
197 if (!(value is SqlConnection
))
198 throw new InvalidCastException ("The value was not a valid SqlConnection.");
199 Connection
= (SqlConnection
) value;
203 IDataParameterCollection IDbCommand
.Parameters
{
204 get { return Parameters; }
207 IDbTransaction IDbCommand
.Transaction
{
208 get { return Transaction; }
210 if (!(value is SqlTransaction
))
211 throw new ArgumentException ();
212 Transaction
= (SqlTransaction
) value;
217 [DataSysDescription ("The transaction used by the command.")]
218 [DesignerSerializationVisibility (DesignerSerializationVisibility
.Hidden
)]
219 public SqlTransaction Transaction
{
220 get { return transaction; }
221 set { transaction = value; }
224 [DataCategory ("Behavior")]
225 [DataSysDescription ("When used by a DataAdapter.Update, how command results are applied to the current DataRow.")]
226 [DefaultValue (UpdateRowSource
.Both
)]
227 public UpdateRowSource UpdatedRowSource
{
228 get { return updatedRowSource; }
229 set { updatedRowSource = value; }
236 public void Cancel ()
238 if (Connection
== null || Connection
.Tds
== null)
240 Connection
.Tds
.Cancel ();
243 internal void CloseDataReader (bool moreResults
)
245 Connection
.DataReader
= null;
247 if ((behavior
& CommandBehavior
.CloseConnection
) != 0)
251 public SqlParameter
CreateParameter ()
253 return new SqlParameter ();
256 internal void DeriveParameters ()
258 if (commandType
!= CommandType
.StoredProcedure
)
259 throw new InvalidOperationException (String
.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType
));
260 ValidateCommand ("DeriveParameters");
262 SqlParameterCollection localParameters
= new SqlParameterCollection (this);
263 localParameters
.Add ("@P1", SqlDbType
.NVarChar
, commandText
.Length
).Value
= commandText
;
265 string sql
= "sp_procedure_params_rowset";
267 Connection
.Tds
.ExecProc (sql
, localParameters
.MetaParameters
, 0, true);
269 SqlDataReader reader
= new SqlDataReader (this);
271 object[] dbValues
= new object[reader
.FieldCount
];
273 while (reader
.Read ()) {
274 reader
.GetValues (dbValues
);
275 parameters
.Add (new SqlParameter (dbValues
));
280 private void Execute (CommandBehavior behavior
, bool wantResults
)
282 TdsMetaParameterCollection parms
= Parameters
.MetaParameters
;
283 if (preparedStatement
== null) {
284 bool schemaOnly
= ((behavior
& CommandBehavior
.SchemaOnly
) > 0);
285 bool keyInfo
= ((behavior
& CommandBehavior
.KeyInfo
) > 0);
287 StringBuilder sql1
= new StringBuilder ();
288 StringBuilder sql2
= new StringBuilder ();
290 if (schemaOnly
|| keyInfo
)
291 sql1
.Append ("SET FMTONLY OFF;");
293 sql1
.Append ("SET NO_BROWSETABLE ON;");
294 sql2
.Append ("SET NO_BROWSETABLE OFF;");
297 sql1
.Append ("SET FMTONLY ON;");
298 sql2
.Append ("SET FMTONLY OFF;");
301 switch (CommandType
) {
302 case CommandType
.StoredProcedure
:
303 if (keyInfo
|| schemaOnly
)
304 Connection
.Tds
.Execute (sql1
.ToString ());
305 Connection
.Tds
.ExecProc (CommandText
, parms
, CommandTimeout
, wantResults
);
306 if (keyInfo
|| schemaOnly
)
307 Connection
.Tds
.Execute (sql2
.ToString ());
309 case CommandType
.Text
:
310 string sql
= String
.Format ("{0}{1}{2}", sql1
.ToString (), CommandText
, sql2
.ToString ());
311 Connection
.Tds
.Execute (sql
, parms
, CommandTimeout
, wantResults
);
316 Connection
.Tds
.ExecPrepared (preparedStatement
, parms
, CommandTimeout
, wantResults
);
319 public int ExecuteNonQuery ()
321 ValidateCommand ("ExecuteNonQuery");
325 Execute (CommandBehavior
.Default
, false);
326 if (commandType
== CommandType
.StoredProcedure
)
329 // .NET documentation says that except for INSERT, UPDATE and
330 // DELETE where the return value is the number of rows affected
331 // for the rest of the commands the return value is -1.
332 if ((CommandText
.ToUpper().IndexOf("UPDATE")!=-1) ||
333 (CommandText
.ToUpper().IndexOf("INSERT")!=-1) ||
334 (CommandText
.ToUpper().IndexOf("DELETE")!=-1))
335 result
= Connection
.Tds
.RecordsAffected
;
340 catch (TdsTimeoutException e
) {
341 throw SqlException
.FromTdsInternalException ((TdsInternalException
) e
);
344 GetOutputParameters ();
348 public SqlDataReader
ExecuteReader ()
350 return ExecuteReader (CommandBehavior
.Default
);
353 public SqlDataReader
ExecuteReader (CommandBehavior behavior
)
355 ValidateCommand ("ExecuteReader");
357 Execute (behavior
, true);
359 catch (TdsTimeoutException e
) {
360 throw SqlException
.FromTdsInternalException ((TdsInternalException
) e
);
363 Connection
.DataReader
= new SqlDataReader (this);
364 return Connection
.DataReader
;
367 public object ExecuteScalar ()
369 ValidateCommand ("ExecuteScalar");
371 Execute (CommandBehavior
.Default
, true);
373 catch (TdsTimeoutException e
) {
374 throw SqlException
.FromTdsInternalException ((TdsInternalException
) e
);
377 if (!Connection
.Tds
.NextResult () || !Connection
.Tds
.NextRow ())
380 object result
= Connection
.Tds
.ColumnValues
[0];
381 CloseDataReader (true);
385 public XmlReader
ExecuteXmlReader ()
387 ValidateCommand ("ExecuteXmlReader");
389 Execute (CommandBehavior
.Default
, true);
391 catch (TdsTimeoutException e
) {
392 throw SqlException
.FromTdsInternalException ((TdsInternalException
) e
);
395 SqlDataReader dataReader
= new SqlDataReader (this);
396 SqlXmlTextReader textReader
= new SqlXmlTextReader (dataReader
);
397 XmlReader xmlReader
= new XmlTextReader (textReader
);
401 internal void GetOutputParameters ()
403 IList list
= Connection
.Tds
.OutputParameters
;
405 if (list
!= null && list
.Count
> 0) {
408 foreach (SqlParameter parameter
in parameters
) {
409 if (parameter
.Direction
!= ParameterDirection
.Input
) {
410 parameter
.Value
= list
[index
];
413 if (index
>= list
.Count
)
419 object ICloneable
.Clone ()
421 return new SqlCommand (commandText
, connection
, transaction
, commandType
, updatedRowSource
, designTimeVisible
, commandTimeout
, parameters
);
425 IDbDataParameter IDbCommand
.CreateParameter ()
427 return CreateParameter ();
430 IDataReader IDbCommand
.ExecuteReader ()
432 return ExecuteReader ();
435 IDataReader IDbCommand
.ExecuteReader (CommandBehavior behavior
)
437 return ExecuteReader (behavior
);
440 public void Prepare ()
442 ValidateCommand ("Prepare");
443 if (CommandType
== CommandType
.Text
)
444 preparedStatement
= Connection
.Tds
.Prepare (CommandText
, Parameters
.MetaParameters
);
447 public void ResetCommandTimeout ()
452 private void Unprepare ()
454 Connection
.Tds
.Unprepare (preparedStatement
);
455 preparedStatement
= null;
458 private void ValidateCommand (string method
)
460 if (Connection
== null)
461 throw new InvalidOperationException (String
.Format ("{0} requires a Connection object to continue.", method
));
462 if (Connection
.Transaction
!= null && transaction
!= Connection
.Transaction
)
463 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
464 if (Connection
.State
!= ConnectionState
.Open
)
465 throw new InvalidOperationException (String
.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method
));
466 if (commandText
== String
.Empty
|| commandText
== null)
467 throw new InvalidOperationException ("The command text for this Command has not been set.");
468 if (Connection
.DataReader
!= null)
469 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
470 if (Connection
.XmlReader
!= null)
471 throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
474 #endregion // Methods