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")]
138 get { return timeout; }
141 throw new ArgumentException ("The property value assigned is less than 0.",
147 [OdbcCategory ("Data")]
148 [DefaultValue ("Text")]
149 [OdbcDescriptionAttribute ("How to interpret the CommandText")]
150 [RefreshPropertiesAttribute (RefreshProperties.All)]
155 CommandType CommandType {
156 get { return commandType; }
158 ExceptionHelper.CheckEnumValue (typeof (CommandType), value);
164 [OdbcCategory ("Behavior")]
165 [OdbcDescriptionAttribute ("Connection used by the command")]
166 [DefaultValue (null)]
167 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
168 public OdbcConnection Connection {
169 get { return connection; }
170 set { connection = value; }
175 [DefaultValue (null)]
176 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
177 public new OdbcConnection Connection {
178 get { return DbConnection as OdbcConnection; }
179 set { DbConnection = value; }
183 [BrowsableAttribute (false)]
184 [DesignOnlyAttribute (true)]
185 [DefaultValue (true)]
187 [EditorBrowsable (EditorBrowsableState.Never)]
193 bool DesignTimeVisible {
194 get { return designTimeVisible; }
195 set { designTimeVisible = value; }
198 [OdbcCategory ("Data")]
199 [OdbcDescriptionAttribute ("The parameters collection")]
200 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
205 OdbcParameterCollection Parameters {
210 return base.Parameters as OdbcParameterCollection;
215 [BrowsableAttribute (false)]
216 [OdbcDescriptionAttribute ("The transaction used by the command")]
217 [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
222 OdbcTransaction Transaction {
223 get { return transaction; }
224 set { transaction = value; }
227 [OdbcCategory ("Behavior")]
228 [DefaultValue (UpdateRowSource.Both)]
229 [OdbcDescriptionAttribute ("When used by a DataAdapter.Update, how command results are applied to the current DataRow")]
234 UpdateRowSource UpdatedRowSource {
235 get { return updateRowSource; }
237 ExceptionHelper.CheckEnumValue (typeof (UpdateRowSource), value);
238 updateRowSource = value;
243 protected override DbConnection DbConnection {
244 get { return connection; }
245 set { connection = (OdbcConnection) value;}
251 IDbConnection IDbCommand.Connection {
252 get { return Connection; }
253 set { Connection = (OdbcConnection) value; }
256 IDataParameterCollection IDbCommand.Parameters {
257 get { return Parameters; }
260 protected override DbParameterCollection DbParameterCollection {
261 get { return _parameters as DbParameterCollection;}
266 IDbTransaction IDbCommand.Transaction {
267 get { return (IDbTransaction) Transaction; }
269 if (value is OdbcTransaction) {
270 Transaction = (OdbcTransaction) value;
272 throw new ArgumentException ();
277 protected override DbTransaction DbTransaction {
278 get { return transaction; }
279 set { transaction = (OdbcTransaction) value; }
283 #endregion // Properties
293 if (hstmt != IntPtr.Zero) {
294 OdbcReturn Ret = libodbc.SQLCancel (hstmt);
295 if (Ret != OdbcReturn.Success && Ret != OdbcReturn.SuccessWithInfo)
296 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
298 throw new InvalidOperationException ();
302 IDbDataParameter IDbCommand.CreateParameter ()
304 return CreateParameter ();
308 protected override DbParameter CreateDbParameter ()
310 return CreateParameter ();
314 public new OdbcParameter CreateParameter ()
316 return new OdbcParameter ();
319 internal void Unlink ()
324 FreeStatement (false);
327 protected override void Dispose (bool disposing)
332 FreeStatement (); // free handles
342 private IntPtr ReAllocStatment ()
346 if (hstmt != IntPtr.Zero)
347 // Free the existing hstmt. Also unlinks from the connection.
349 // Link this command to the connection. The hstmt created below
350 // only remains valid while generation == Connection.generation.
351 generation = Connection.Link (this);
352 ret = libodbc.SQLAllocHandle (OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
353 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
354 throw connection.CreateOdbcException (OdbcHandleType.Dbc, Connection.hDbc);
359 void FreeStatement ()
361 FreeStatement (true);
364 private void FreeStatement (bool unlink)
368 if (hstmt == IntPtr.Zero)
371 // Normally the command is unlinked from the connection, but during
372 // OdbcConnection.Close() this would be pointless and (quadratically)
375 Connection.Unlink (this);
377 // Serialize with respect to the connection's own destruction
379 // If the connection has already called SQLDisconnect then hstmt
380 // may have already been freed, in which case it is not safe to
381 // use. Thus the generation check.
382 if(Connection.Generation == generation) {
383 // free previously allocated handle.
384 OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
385 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
386 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
388 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, hstmt);
389 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
390 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
396 private void ExecSQL (CommandBehavior behavior, bool createReader, string sql)
400 if (!prepared && Parameters.Count == 0) {
403 ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS);
404 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo && ret != OdbcReturn.NoData)
405 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
413 ret = libodbc.SQLExecute (hstmt);
414 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
415 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
418 internal void FreeIfNotPrepared ()
428 int ExecuteNonQuery ()
430 return ExecuteNonQuery ("ExecuteNonQuery", CommandBehavior.Default, false);
433 private int ExecuteNonQuery (string method, CommandBehavior behavior, bool createReader)
436 if (Connection == null)
437 throw new InvalidOperationException (string.Format (
438 "{0}: Connection is not set.", method));
439 if (Connection.State == ConnectionState.Closed)
440 throw new InvalidOperationException (string.Format (
441 "{0}: Connection state is closed", method));
442 if (CommandText.Length == 0)
443 throw new InvalidOperationException (string.Format (
444 "{0}: CommandText is not set.", method));
446 ExecSQL (behavior, createReader, CommandText);
448 // .NET documentation says that except for INSERT, UPDATE and
449 // DELETE where the return value is the number of rows affected
450 // for the rest of the commands the return value is -1.
451 if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) ||
452 (CommandText.ToUpper().IndexOf("INSERT")!=-1) ||
453 (CommandText.ToUpper().IndexOf("DELETE")!=-1)) {
455 libodbc.SQLRowCount (hstmt, ref numrows);
460 if (!createReader && !prepared)
475 ret = libodbc.SQLPrepare(hstmt, CommandText, CommandText.Length);
476 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
477 throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
481 private void BindParameters ()
484 foreach (OdbcParameter p in Parameters) {
485 p.Bind (this, hstmt, i);
495 OdbcDataReader ExecuteReader ()
497 return ExecuteReader (CommandBehavior.Default);
501 IDataReader IDbCommand.ExecuteReader ()
503 return ExecuteReader ();
506 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
508 return ExecuteReader (behavior);
516 OdbcDataReader ExecuteReader (CommandBehavior behavior)
518 return ExecuteReader ("ExecuteReader", behavior);
521 OdbcDataReader ExecuteReader (string method, CommandBehavior behavior)
523 int recordsAffected = ExecuteNonQuery (method, behavior, true);
524 OdbcDataReader dataReader = new OdbcDataReader (this, behavior, recordsAffected);
529 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
531 return ExecuteReader (behavior);
539 object ExecuteScalar ()
542 OdbcDataReader reader = ExecuteReader ("ExecuteScalar",
543 CommandBehavior.Default);
553 object ICloneable.Clone ()
555 OdbcCommand command = new OdbcCommand ();
556 command.CommandText = this.CommandText;
557 command.CommandTimeout = this.CommandTimeout;
558 command.CommandType = this.CommandType;
559 command.Connection = this.Connection;
560 command.DesignTimeVisible = this.DesignTimeVisible;
561 foreach (OdbcParameter parameter in this.Parameters)
562 command.Parameters.Add (parameter);
563 command.Transaction = this.Transaction;
567 public void ResetCommandTimeout ()
569 CommandTimeout = DEFAULT_COMMAND_TIMEOUT;