Copied remotely
[mono.git] / mcs / class / Mono.Data.TdsClient / Mono.Data.TdsClient / TdsCommand.cs
1 //
2 // Mono.Data.TdsClient.TdsCommand.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //
9 // (C) Ximian, Inc 2002 http://www.ximian.com/
10 // (C) Daniel Morgan, 2002
11 // Copyright (C) Tim Coleman, 2002
12 //
13
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using Mono.Data.Tds;
36 using Mono.Data.Tds.Protocol;
37 using System;
38 using System.Collections;
39 using System.Collections.Specialized;
40 using System.ComponentModel;
41 using System.Data;
42 using System.Data.Common;
43 using System.Runtime.InteropServices;
44 using System.Text;
45
46 namespace Mono.Data.TdsClient {
47         public sealed class TdsCommand : Component, IDbCommand, ICloneable
48         {
49                 #region Fields
50
51                 bool disposed = false;
52                 int commandTimeout;
53                 bool designTimeVisible;
54                 string commandText;
55                 CommandType commandType;
56                 TdsConnection connection;
57                 TdsTransaction transaction;
58                 UpdateRowSource updatedRowSource;
59                 CommandBehavior behavior = CommandBehavior.Default;
60                 TdsParameterCollection parameters;
61
62                 #endregion // Fields
63
64                 #region Constructors
65
66                 public TdsCommand() 
67                         : this (String.Empty, null, null)
68                 {
69                 }
70
71                 public TdsCommand (string commandText) 
72                         : this (commandText, null, null)
73                 {
74                         commandText = commandText;
75                 }
76
77                 public TdsCommand (string commandText, TdsConnection connection) 
78                         : this (commandText, connection, null)
79                 {
80                         Connection = connection;
81                 }
82
83                 public TdsCommand (string commandText, TdsConnection connection, TdsTransaction transaction) 
84                 {
85                         this.commandText = commandText;
86                         this.connection = connection;
87                         this.transaction = transaction;
88                         this.commandType = CommandType.Text;
89                         this.updatedRowSource = UpdateRowSource.Both;
90
91                         this.designTimeVisible = false;
92                         this.commandTimeout = 30;
93                         parameters = new TdsParameterCollection (this);
94                 }
95
96                 #endregion // Constructors
97
98                 #region Properties
99
100                 internal CommandBehavior CommandBehavior {
101                         get { return behavior; }
102                 }
103
104                 public string CommandText {
105                         get { return commandText; }
106                         set { commandText = value; }
107                 }
108
109                 public int CommandTimeout {
110                         get { return commandTimeout;  }
111                         set { 
112                                 if (commandTimeout < 0)
113                                         throw new ArgumentException ("The property value assigned is less than 0.");
114                                 commandTimeout = value; 
115                         }
116                 }
117
118                 public CommandType CommandType  {
119                         get { return commandType; }
120                         set { 
121                                 if (value == CommandType.TableDirect)
122                                         throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono TdsClient Data Provider.");
123                                 commandType = value; 
124                         }
125                 }
126
127                 public TdsConnection Connection {
128                         get { return connection; }
129                         set { 
130                                 if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
131                                         throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
132                                 transaction = null;
133                                 connection = value; 
134                         }
135                 }
136
137                 public bool DesignTimeVisible {
138                         get { return designTimeVisible; } 
139                         set { designTimeVisible = value; }
140                 }
141
142                 public TdsParameterCollection Parameters {
143                         get { return parameters; }
144                 }
145
146                 internal ITds Tds {
147                         get { return Connection.Tds; }
148                 }
149
150                 IDbConnection IDbCommand.Connection {
151                         get { return Connection; }
152                         set { 
153                                 if (!(value is TdsConnection))
154                                         throw new InvalidCastException ("The value was not a valid TdsConnection.");
155                                 Connection = (TdsConnection) value;
156                         }
157                 }
158
159                 IDataParameterCollection IDbCommand.Parameters  {
160                         get { return Parameters; }
161                 }
162
163                 IDbTransaction IDbCommand.Transaction {
164                         get { return Transaction; }
165                         set { 
166                                 if (!(value is TdsTransaction))
167                                         throw new ArgumentException ();
168                                 Transaction = (TdsTransaction) value; 
169                         }
170                 }
171
172                 public TdsTransaction Transaction {
173                         get { return transaction; }
174                         set { transaction = value; }
175                 }       
176
177                 public UpdateRowSource UpdatedRowSource {
178                         get { return updatedRowSource; }
179                         set { updatedRowSource = value; }
180                 }
181
182                 #endregion // Fields
183
184                 #region Methods
185
186                 public void Cancel () 
187                 {
188                         if (Connection == null || Connection.Tds == null)
189                                 return;
190                         Connection.Tds.Cancel ();
191                 }
192
193                 internal void CloseDataReader (bool moreResults)
194                 {
195                         GetOutputParameters ();
196                         Connection.DataReader = null;
197
198                         if ((behavior & CommandBehavior.CloseConnection) != 0)
199                                 Connection.Close ();
200                 }
201
202                 public TdsParameter CreateParameter () 
203                 {
204                         return new TdsParameter ();
205                 }
206
207                 internal void DeriveParameters ()
208                 {
209                         if (commandType != CommandType.StoredProcedure)
210                                 throw new InvalidOperationException (String.Format ("TdsCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
211                         ValidateCommand ("DeriveParameters");
212
213                         TdsParameterCollection localParameters = new TdsParameterCollection (this);
214                         localParameters.Add ("@P1", TdsType.NVarChar, commandText.Length).Value = commandText;
215
216                         string sql = "sp_procedure_params_rowset";
217
218                         Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
219
220                         TdsDataReader reader = new TdsDataReader (this);
221                         parameters.Clear ();
222                         object[] dbValues = new object[reader.FieldCount];
223
224                         while (reader.Read ()) {
225                                 reader.GetValues (dbValues);
226                                 parameters.Add (new TdsParameter (dbValues));
227                         }
228                         reader.Close ();        
229                 }
230
231                 private void Execute (CommandBehavior behavior, bool wantResults)
232                 {
233                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
234                         bool schemaOnly = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
235                         bool keyInfo = ((CommandBehavior & CommandBehavior.SchemaOnly) > 0);
236
237                         StringBuilder sql1 = new StringBuilder ();
238                         StringBuilder sql2 = new StringBuilder ();
239
240                         if (schemaOnly || keyInfo)
241                                 sql1.Append ("SET FMTONLY OFF;");
242                         if (keyInfo) {
243                                 sql1.Append ("SET NO_BROWSETABLE ON;");
244                                 sql2.Append ("SET NO_BROWSETABLE OFF;");
245                         }
246                         if (schemaOnly) {
247                                 sql1.Append ("SET FMTONLY ON;");
248                                 sql2.Append ("SET FMTONLY OFF;");
249                         }
250
251                         switch (CommandType) {
252                         case CommandType.StoredProcedure:
253                                 if (keyInfo || schemaOnly)
254                                         Connection.Tds.Execute (sql1.ToString ());
255                                 Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
256                                 if (keyInfo || schemaOnly)
257                                         Connection.Tds.Execute (sql2.ToString ());
258                                 break;
259                         case CommandType.Text:
260                                 string sql = String.Format ("{0}{1}{2}", sql1.ToString (), CommandText, sql2.ToString ());
261                                 Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
262                                 break;
263                         }
264                 }
265
266                 public int ExecuteNonQuery ()
267                 {
268                         ValidateCommand ("ExecuteNonQuery");
269                         int result = 0;
270
271                         try {
272                                 Execute (CommandBehavior.Default, false);
273                         }
274                         catch (TdsTimeoutException e) {
275                                 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
276                         }
277
278                         GetOutputParameters ();
279                         return result;
280                 }
281
282                 public TdsDataReader ExecuteReader ()
283                 {
284                         return ExecuteReader (CommandBehavior.Default);
285                 }
286
287                 public TdsDataReader ExecuteReader (CommandBehavior behavior)
288                 {
289                         ValidateCommand ("ExecuteReader");
290                         try {
291                                 Execute (behavior, true);
292                         }
293                         catch (TdsTimeoutException e) {
294                                 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
295                         }
296                         Connection.DataReader = new TdsDataReader (this);
297                         return Connection.DataReader;
298                 }
299
300                 public object ExecuteScalar ()
301                 {
302                         ValidateCommand ("ExecuteScalar");
303                         try {
304                                 Execute (CommandBehavior.Default, true);
305                         }
306                         catch (TdsTimeoutException e) {
307                                 throw TdsException.FromTdsInternalException ((TdsInternalException) e);
308                         }
309
310                         if (!Connection.Tds.NextResult () || !Connection.Tds.NextRow ())
311                                 return null;
312
313                         object result = Connection.Tds.ColumnValues [0];
314                         CloseDataReader (true);
315                         return result;
316                 }
317
318                 private void GetOutputParameters ()
319                 {
320                         Connection.Tds.SkipToEnd ();
321
322                         IList list = Connection.Tds.ColumnValues;
323
324                         if (list != null && list.Count > 0) {
325                                 int index = 0;
326                                 foreach (TdsParameter parameter in parameters) {
327                                         if (parameter.Direction != ParameterDirection.Input) {
328                                                 parameter.Value = list [index];
329                                                 index += 1;
330                                         }
331                                         if (index >= list.Count)
332                                                 break;
333                                 }
334                         }
335                 }
336
337                 object ICloneable.Clone ()
338                 {
339                         return new TdsCommand (commandText, Connection);
340                 }
341
342                 IDbDataParameter IDbCommand.CreateParameter ()
343                 {
344                         return CreateParameter ();
345                 }
346
347                 IDataReader IDbCommand.ExecuteReader ()
348                 {
349                         return ExecuteReader ();
350                 }
351
352                 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
353                 {
354                         return ExecuteReader (behavior);
355                 }
356
357                 public void Prepare ()
358                 {
359                         throw new NotSupportedException ("TdsClient does not support PREPARE.");
360                 }
361
362                 public void ResetCommandTimeout ()
363                 {
364                         commandTimeout = 30;
365                 }
366
367                 private void ValidateCommand (string method)
368                 {
369                         if (Connection == null)
370                                 throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
371                         if (Connection.Transaction != null && transaction != Connection.Transaction)
372                                 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
373                         if (Connection.State != ConnectionState.Open)
374                                 throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
375                         if (commandText == String.Empty || commandText == null)
376                                 throw new InvalidOperationException ("The command text for this Command has not been set.");
377                         if (Connection.DataReader != null)
378                                 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
379                 }
380
381                 #endregion // Methods
382         }
383 }