2 // System.Data.Odbc.OdbcCommand
5 // Brian Ritchie (brianlritchie@hotmail.com)
7 // Copyright (C) Brian Ritchie, 2002
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.ComponentModel;
36 using System.Data.Common;
37 using System.Collections;
38 using System.Runtime.InteropServices;
40 namespace System.Data.Odbc
43 /// Represents an SQL statement or stored procedure to execute against a data source.
45 [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
46 [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
48 [DefaultEvent ("RecordsAffected")]
49 public sealed class OdbcCommand : DbCommand, ICloneable
51 public sealed class OdbcCommand : Component, ICloneable, IDbCommand
56 const int DEFAULT_COMMAND_TIMEOUT = 30;
60 CommandType commandType;
61 UpdateRowSource updateRowSource;
63 OdbcConnection connection;
64 OdbcTransaction transaction;
65 OdbcParameterCollection _parameters;
67 bool designTimeVisible;
69 IntPtr hstmt = IntPtr.Zero;
70 object generation = null; // validity of hstmt
80 timeout = DEFAULT_COMMAND_TIMEOUT;
81 commandType = CommandType.Text;
82 _parameters = new OdbcParameterCollection ();
83 designTimeVisible = true;
84 updateRowSource = UpdateRowSource.Both;
87 public OdbcCommand (string cmdText) : this ()
89 commandText = cmdText;
92 public OdbcCommand (string cmdText, OdbcConnection connection)
95 Connection = connection;
98 public OdbcCommand (string cmdText, OdbcConnection connection,
99 OdbcTransaction transaction) : this (cmdText, connection)
101 this.Transaction = transaction;
104 #endregion // Constructors
108 internal IntPtr hStmt {
109 get { return hstmt; }
112 [OdbcCategory ("Data")]
114 [OdbcDescriptionAttribute ("Command text to execute")]
115 [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
116 [RefreshPropertiesAttribute (RefreshProperties.All)]
123 if (commandText == null)
135 [OdbcDescriptionAttribute ("Time to wait for command to execute")]
136 #if NET_1_0 || ONLY_1_1
137 [DefaultValue (DEFAULT_COMMAND_TIMEOUT)]
144 get { return timeout; }
148 throw new ArgumentException ("The property value assigned is less than 0.",
151 throw new ArgumentException ("The property value assigned is less than 0.");
157 [OdbcCategory ("Data")]
158 [DefaultValue ("Text")]
159 [OdbcDescriptionAttribute ("How to interpret the CommandText")]
160 [RefreshPropertiesAttribute (RefreshProperties.All)]
165 CommandType CommandType {
166 get { return commandType; }
168 ExceptionHelper.CheckEnumValue (typeof (CommandType), value);
174 [OdbcCategory ("Behavior")]
175 [OdbcDescriptionAttribute ("Connection used by the command")]
176 [DefaultValue (null)]
177 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
178 public OdbcConnection Connection {
179 get { return connection; }
180 set { connection = value; }
185 [DefaultValue (null)]
186 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
187 public new OdbcConnection Connection {
188 get { return DbConnection as OdbcConnection; }
189 set { DbConnection = value; }
193 [BrowsableAttribute (false)]
194 [DesignOnlyAttribute (true)]
195 [DefaultValue (true)]
197 [EditorBrowsable (EditorBrowsableState.Never)]
203 bool DesignTimeVisible {
204 get { return designTimeVisible; }
205 set { designTimeVisible = value; }
208 [OdbcCategory ("Data")]
209 [OdbcDescriptionAttribute ("The parameters collection")]
210 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
215 OdbcParameterCollection Parameters {
220 return base.Parameters as OdbcParameterCollection;
225 [BrowsableAttribute (false)]
226 [OdbcDescriptionAttribute ("The transaction used by the command")]
227 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
232 OdbcTransaction Transaction {
233 get { return transaction; }
234 set { transaction = value; }
237 [OdbcCategory ("Behavior")]
238 [DefaultValue (UpdateRowSource.Both)]
239 [OdbcDescriptionAttribute ("When used by a DataAdapter.Update, how command results are applied to the current DataRow")]
244 UpdateRowSource UpdatedRowSource {
245 get { return updateRowSource; }
247 ExceptionHelper.CheckEnumValue (typeof (UpdateRowSource), value);
248 updateRowSource = value;
253 protected override DbConnection DbConnection {
254 get { return connection; }
255 set { connection = (OdbcConnection) value;}
261 IDbConnection IDbCommand.Connection {
262 get { return Connection; }
263 set { Connection = (OdbcConnection) value; }
266 IDataParameterCollection IDbCommand.Parameters {
267 get { return Parameters; }
270 protected override DbParameterCollection DbParameterCollection {
271 get { return _parameters as DbParameterCollection;}
276 IDbTransaction IDbCommand.Transaction {
277 get { return (IDbTransaction) Transaction; }
279 if (value is OdbcTransaction) {
280 Transaction = (OdbcTransaction) value;
282 throw new ArgumentException ();
287 protected override DbTransaction DbTransaction {
288 get { return transaction; }
289 set { transaction = (OdbcTransaction) value; }
293 #endregion // Properties
303 if (hstmt != IntPtr.Zero) {
304 OdbcReturn Ret = libodbc.SQLCancel (hstmt);
305 if (Ret != OdbcReturn.Success && Ret != OdbcReturn.SuccessWithInfo)
306 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
308 throw new InvalidOperationException ();
312 IDbDataParameter IDbCommand.CreateParameter ()
314 return CreateParameter ();
318 protected override DbParameter CreateDbParameter ()
320 return CreateParameter ();
324 public new OdbcParameter CreateParameter ()
326 return new OdbcParameter ();
329 internal void Unlink ()
334 FreeStatement (false);
337 protected override void Dispose (bool disposing)
342 FreeStatement (); // free handles
352 private IntPtr ReAllocStatment ()
356 if (hstmt != IntPtr.Zero)
357 // Free the existing hstmt. Also unlinks from the connection.
359 // Link this command to the connection. The hstmt created below
360 // only remains valid while generation == Connection.generation.
361 generation = Connection.Link (this);
362 ret = libodbc.SQLAllocHandle (OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
363 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
364 throw connection.CreateOdbcException (OdbcHandleType.Dbc, Connection.hDbc);
369 void FreeStatement ()
371 FreeStatement (true);
374 private void FreeStatement (bool unlink)
378 if (hstmt == IntPtr.Zero)
381 // Normally the command is unlinked from the connection, but during
382 // OdbcConnection.Close() this would be pointless and (quadratically)
385 Connection.Unlink (this);
387 // Serialize with respect to the connection's own destruction
389 // If the connection has already called SQLDisconnect then hstmt
390 // may have already been freed, in which case it is not safe to
391 // use. Thus the generation check.
392 if(Connection.Generation == generation) {
393 // free previously allocated handle.
394 OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
395 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
396 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
398 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, hstmt);
399 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
400 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
406 private void ExecSQL (CommandBehavior behavior, bool createReader, string sql)
410 if (!prepared && Parameters.Count == 0) {
413 ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS);
414 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo && ret != OdbcReturn.NoData)
415 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
423 ret = libodbc.SQLExecute (hstmt);
424 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
425 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
428 internal void FreeIfNotPrepared ()
438 int ExecuteNonQuery ()
440 return ExecuteNonQuery ("ExecuteNonQuery", CommandBehavior.Default, false);
443 private int ExecuteNonQuery (string method, CommandBehavior behavior, bool createReader)
446 if (Connection == null)
447 throw new InvalidOperationException (string.Format (
448 "{0}: Connection is not set.", method));
449 if (Connection.State == ConnectionState.Closed)
450 throw new InvalidOperationException (string.Format (
451 "{0}: Connection state is closed", method));
452 if (CommandText.Length == 0)
453 throw new InvalidOperationException (string.Format (
454 "{0}: CommandText is not set.", method));
456 ExecSQL (behavior, createReader, CommandText);
458 // .NET documentation says that except for INSERT, UPDATE and
459 // DELETE where the return value is the number of rows affected
460 // for the rest of the commands the return value is -1.
461 if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) ||
462 (CommandText.ToUpper().IndexOf("INSERT")!=-1) ||
463 (CommandText.ToUpper().IndexOf("DELETE")!=-1)) {
465 libodbc.SQLRowCount (hstmt, ref numrows);
470 if (!createReader && !prepared)
485 ret = libodbc.SQLPrepare(hstmt, CommandText, CommandText.Length);
486 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
487 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
491 private void BindParameters ()
494 foreach (OdbcParameter p in Parameters) {
495 p.Bind (this, hstmt, i);
505 OdbcDataReader ExecuteReader ()
507 return ExecuteReader (CommandBehavior.Default);
511 IDataReader IDbCommand.ExecuteReader ()
513 return ExecuteReader ();
516 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
518 return ExecuteReader (behavior);
526 OdbcDataReader ExecuteReader (CommandBehavior behavior)
528 return ExecuteReader ("ExecuteReader", behavior);
531 OdbcDataReader ExecuteReader (string method, CommandBehavior behavior)
533 int recordsAffected = ExecuteNonQuery (method, behavior, true);
534 OdbcDataReader dataReader = new OdbcDataReader (this, behavior, recordsAffected);
539 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
541 return ExecuteReader (behavior);
549 object ExecuteScalar ()
552 OdbcDataReader reader = ExecuteReader ("ExecuteScalar",
553 CommandBehavior.Default);
563 object ICloneable.Clone ()
565 OdbcCommand command = new OdbcCommand ();
566 command.CommandText = this.CommandText;
567 command.CommandTimeout = this.CommandTimeout;
568 command.CommandType = this.CommandType;
569 command.Connection = this.Connection;
570 command.DesignTimeVisible = this.DesignTimeVisible;
571 foreach (OdbcParameter parameter in this.Parameters)
572 command.Parameters.Add (parameter);
573 command.Transaction = this.Transaction;
577 public void ResetCommandTimeout ()
579 CommandTimeout = DEFAULT_COMMAND_TIMEOUT;