2 // Mono.Data.TdsClient.TdsCommand.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
7 // Tim Coleman (tim@timcoleman.com)
9 // (C) Ximian, Inc 2002 http://www.ximian.com/
10 // (C) Daniel Morgan, 2002
11 // Copyright (C) Tim Coleman, 2002
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using Mono.Data.Tds.Protocol;
37 using MDTP = Mono.Data.Tds.Protocol;
39 using System.Collections;
40 using System.Collections.Specialized;
41 using System.ComponentModel;
43 using System.Data.Common;
44 using System.Runtime.InteropServices;
47 namespace Mono.Data.TdsClient {
48 public sealed class TdsCommand : Component, IDbCommand, ICloneable
52 bool disposed = false;
54 bool designTimeVisible;
56 CommandType commandType;
57 TdsConnection connection;
58 TdsTransaction transaction;
59 UpdateRowSource updatedRowSource;
60 CommandBehavior behavior = CommandBehavior.Default;
61 TdsParameterCollection parameters;
68 : this (String.Empty, null, null)
72 public TdsCommand (string commandText)
73 : this (commandText, null, null)
77 public TdsCommand (string commandText, TdsConnection connection)
78 : this (commandText, connection, null)
82 public TdsCommand (string commandText, TdsConnection connection, TdsTransaction transaction)
84 this.commandText = commandText;
85 this.connection = connection;
86 this.transaction = transaction;
87 this.commandType = CommandType.Text;
88 this.updatedRowSource = UpdateRowSource.Both;
90 this.designTimeVisible = false;
91 this.commandTimeout = 30;
92 parameters = new TdsParameterCollection (this);
95 #endregion // Constructors
99 internal CommandBehavior CommandBehavior {
100 get { return behavior; }
103 public string CommandText {
104 get { return commandText; }
105 set { commandText = value; }
108 public int CommandTimeout {
109 get { return commandTimeout; }
111 if (commandTimeout < 0)
112 throw new ArgumentException ("The property value assigned is less than 0.");
113 commandTimeout = value;
117 public CommandType CommandType {
118 get { return commandType; }
120 if (value == CommandType.TableDirect)
121 throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono TdsClient Data Provider.");
126 public TdsConnection Connection {
127 get { return connection; }
129 if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
130 throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
136 public bool DesignTimeVisible {
137 get { return designTimeVisible; }
138 set { designTimeVisible = value; }
141 public TdsParameterCollection Parameters {
142 get { return parameters; }
145 internal MDTP.Tds Tds {
146 get { return Connection.Tds; }
149 IDbConnection IDbCommand.Connection {
150 get { return Connection; }
152 if (!(value is TdsConnection))
153 throw new InvalidCastException ("The value was not a valid TdsConnection.");
154 Connection = (TdsConnection) value;
158 IDataParameterCollection IDbCommand.Parameters {
159 get { return Parameters; }
162 IDbTransaction IDbCommand.Transaction {
163 get { return Transaction; }
165 if (!(value is TdsTransaction))
166 throw new ArgumentException ();
167 Transaction = (TdsTransaction) value;
171 public TdsTransaction Transaction {
172 get { return transaction; }
173 set { transaction = value; }
176 public UpdateRowSource UpdatedRowSource {
177 get { return updatedRowSource; }
178 set { updatedRowSource = value; }
185 public void Cancel ()
187 if (Connection == null || Connection.Tds == null)
189 Connection.Tds.Cancel ();
192 internal void CloseDataReader (bool moreResults)
194 GetOutputParameters ();
195 Connection.DataReader = null;
197 if ((behavior & CommandBehavior.CloseConnection) != 0)
201 public TdsParameter CreateParameter ()
203 return new TdsParameter ();
206 internal void DeriveParameters ()
208 if (commandType != CommandType.StoredProcedure)
209 throw new InvalidOperationException (String.Format ("TdsCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
210 ValidateCommand ("DeriveParameters");
212 TdsParameterCollection localParameters = new TdsParameterCollection (this);
213 localParameters.Add ("@P1", TdsType.NVarChar, commandText.Length).Value = commandText;
215 string sql = "sp_procedure_params_rowset";
217 Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
219 TdsDataReader reader = new TdsDataReader (this);
221 object[] dbValues = new object[reader.FieldCount];
223 while (reader.Read ()) {
224 reader.GetValues (dbValues);
225 parameters.Add (new TdsParameter (dbValues));
230 private void Execute (CommandBehavior behavior, bool wantResults)
232 TdsMetaParameterCollection parms = Parameters.MetaParameters;
233 bool schemaOnly = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
234 bool keyInfo = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
236 StringBuilder sql1 = new StringBuilder ();
237 StringBuilder sql2 = new StringBuilder ();
239 if (schemaOnly || keyInfo)
240 sql1.Append ("SET FMTONLY OFF;");
242 sql1.Append ("SET NO_BROWSETABLE ON;");
243 sql2.Append ("SET NO_BROWSETABLE OFF;");
246 sql1.Append ("SET FMTONLY ON;");
247 sql2.Append ("SET FMTONLY OFF;");
250 switch (CommandType) {
251 case CommandType.StoredProcedure:
252 if (keyInfo || schemaOnly)
253 Connection.Tds.Execute (sql1.ToString ());
254 Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
255 if (keyInfo || schemaOnly)
256 Connection.Tds.Execute (sql2.ToString ());
258 case CommandType.Text:
259 string sql = String.Format ("{0}{1}{2}", sql1.ToString (), CommandText, sql2.ToString ());
260 Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
265 public int ExecuteNonQuery ()
267 ValidateCommand ("ExecuteNonQuery");
271 Execute (CommandBehavior.Default, false);
273 catch (TdsTimeoutException e) {
274 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
277 GetOutputParameters ();
281 public TdsDataReader ExecuteReader ()
283 return ExecuteReader (CommandBehavior.Default);
286 public TdsDataReader ExecuteReader (CommandBehavior behavior)
288 ValidateCommand ("ExecuteReader");
290 Execute (behavior, true);
292 catch (TdsTimeoutException e) {
293 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
295 Connection.DataReader = new TdsDataReader (this);
296 return Connection.DataReader;
299 public object ExecuteScalar ()
301 ValidateCommand ("ExecuteScalar");
303 Execute (CommandBehavior.Default, true);
305 catch (TdsTimeoutException e) {
306 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
309 if (!Connection.Tds.NextResult () || !Connection.Tds.NextRow ())
312 object result = Connection.Tds.ColumnValues [0];
313 CloseDataReader (true);
317 private void GetOutputParameters ()
319 Connection.Tds.SkipToEnd ();
321 IList list = Connection.Tds.ColumnValues;
323 if (list != null && list.Count > 0) {
325 foreach (TdsParameter parameter in parameters) {
326 if (parameter.Direction != ParameterDirection.Input) {
327 parameter.Value = list [index];
330 if (index >= list.Count)
336 object ICloneable.Clone ()
338 return new TdsCommand (commandText, Connection);
341 IDbDataParameter IDbCommand.CreateParameter ()
343 return CreateParameter ();
346 IDataReader IDbCommand.ExecuteReader ()
348 return ExecuteReader ();
351 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
353 return ExecuteReader (behavior);
356 public void Prepare ()
358 throw new NotSupportedException ("TdsClient does not support PREPARE.");
361 public void ResetCommandTimeout ()
366 private void ValidateCommand (string method)
368 if (Connection == null)
369 throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
370 if (Connection.Transaction != null && transaction != Connection.Transaction)
371 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
372 if (Connection.State != ConnectionState.Open)
373 throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
374 if (commandText == String.Empty || commandText == null)
375 throw new InvalidOperationException ("The command text for this Command has not been set.");
376 if (Connection.DataReader != null)
377 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
380 #endregion // Methods