namespace System.Data.Odbc
{
- /// <summary>
+ /// <summary>
/// Represents an SQL statement or stored procedure to execute against a data source.
/// </summary>
[DesignerAttribute ("Microsoft.VSDesigner.Data.VS.OdbcCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
- [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
+ [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
#if NET_2_0
+ [DefaultEvent ("RecordsAffected")]
public sealed class OdbcCommand : DbCommand, ICloneable
#else
public sealed class OdbcCommand : Component, ICloneable, IDbCommand
{
#region Fields
+ const int DEFAULT_COMMAND_TIMEOUT = 30;
+
string commandText;
int timeout;
CommandType commandType;
- UpdateRowSource updateRowSource = UpdateRowSource.Both;
+ UpdateRowSource updateRowSource;
OdbcConnection connection;
OdbcTransaction transaction;
OdbcParameterCollection _parameters;
bool designTimeVisible;
- bool prepared=false;
+ bool prepared;
IntPtr hstmt = IntPtr.Zero;
+ object generation = null; // validity of hstmt
- bool disposed = false;
+ bool disposed;
#endregion // Fields
public OdbcCommand ()
{
- this.CommandText = String.Empty;
- this.CommandTimeout = 30; // default timeout
- this.CommandType = CommandType.Text;
- Connection = null;
+ timeout = DEFAULT_COMMAND_TIMEOUT;
+ commandType = CommandType.Text;
_parameters = new OdbcParameterCollection ();
- Transaction = null;
- designTimeVisible = false;
-#if ONLY_1_1
+ designTimeVisible = true;
updateRowSource = UpdateRowSource.Both;
-#endif // ONLY_1_1
}
public OdbcCommand (string cmdText) : this ()
{
- CommandText = cmdText;
+ commandText = cmdText;
}
public OdbcCommand (string cmdText, OdbcConnection connection)
Connection = connection;
}
- public OdbcCommand (string cmdText,
- OdbcConnection connection,
+ public OdbcCommand (string cmdText, OdbcConnection connection,
OdbcTransaction transaction) : this (cmdText, connection)
{
this.Transaction = transaction;
#region Properties
- internal IntPtr hStmt
- {
+ internal IntPtr hStmt {
get { return hstmt; }
}
-
- [OdbcCategory ("Data")]
- [DefaultValue ("")]
- [OdbcDescriptionAttribute ("Command text to execute")]
- [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
- [RefreshPropertiesAttribute (RefreshProperties.All)]
- public
+ [OdbcCategory ("Data")]
+ [DefaultValue ("")]
+ [OdbcDescriptionAttribute ("Command text to execute")]
+ [EditorAttribute ("Microsoft.VSDesigner.Data.Odbc.Design.OdbcCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+ [RefreshPropertiesAttribute (RefreshProperties.All)]
+ public
#if NET_2_0
override
#endif
- string CommandText
- {
- get { return commandText; }
- set {
- prepared=false;
+ string CommandText {
+ get {
+ if (commandText == null)
+ return string.Empty;
+ return commandText;
+ }
+ set {
+#if NET_2_0
+ prepared = false;
+#endif
commandText = value;
}
}
[OdbcDescriptionAttribute ("Time to wait for command to execute")]
-#if NET_1_0 || ONLY_1_1
- [DefaultValue (30)]
-#endif
- public
-#if NET_2_0
- override
-#endif
+ public override
int CommandTimeout {
get { return timeout; }
- set { timeout = value; }
+ set {
+ if (value < 0)
+ throw new ArgumentException ("The property value assigned is less than 0.",
+ "CommandTimeout");
+ timeout = value;
+ }
}
[OdbcCategory ("Data")]
- [DefaultValue ("Text")]
- [OdbcDescriptionAttribute ("How to interpret the CommandText")]
- [RefreshPropertiesAttribute (RefreshProperties.All)]
+ [DefaultValue ("Text")]
+ [OdbcDescriptionAttribute ("How to interpret the CommandText")]
+ [RefreshPropertiesAttribute (RefreshProperties.All)]
public
#if NET_2_0
override
#endif
- CommandType CommandType {
+ CommandType CommandType {
get { return commandType; }
- set { commandType = value; }
+ set {
+ ExceptionHelper.CheckEnumValue (typeof (CommandType), value);
+ commandType = value;
+ }
}
#if ONLY_1_1
[OdbcCategory ("Behavior")]
- [OdbcDescriptionAttribute ("Connection used by the command")]
- [DefaultValue (null)]
- [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
- public OdbcConnection Connection {
- get {
- return connection;
- }
- set {
- connection = value;
- }
+ [OdbcDescriptionAttribute ("Connection used by the command")]
+ [DefaultValue (null)]
+ [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+ public OdbcConnection Connection {
+ get { return connection; }
+ set { connection = value; }
}
#endif // ONLY_1_1
#if NET_2_0
- [DefaultValue (null)]
- [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
- public new OdbcConnection Connection
- {
- get { return DbConnection as OdbcConnection; }
- set { DbConnection = value; }
- }
-
+ [DefaultValue (null)]
+ [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+ public new OdbcConnection Connection {
+ get { return DbConnection as OdbcConnection; }
+ set { DbConnection = value; }
+ }
#endif // NET_2_0
[BrowsableAttribute (false)]
- [DesignOnlyAttribute (true)]
- [DefaultValue (true)]
+ [DesignOnlyAttribute (true)]
+ [DefaultValue (true)]
#if NET_2_0
- [EditorBrowsable (EditorBrowsableState.Never)]
-#else
- [Editor ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
+ [EditorBrowsable (EditorBrowsableState.Never)]
#endif
- public
+ public
#if NET_2_0
override
#endif
- bool DesignTimeVisible {
- get {
- return designTimeVisible;
- }
- set {
- designTimeVisible = value;
- }
+ bool DesignTimeVisible {
+ get { return designTimeVisible; }
+ set { designTimeVisible = value; }
}
-
[OdbcCategory ("Data")]
- [OdbcDescriptionAttribute ("The parameters collection")]
- [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
+ [OdbcDescriptionAttribute ("The parameters collection")]
+ [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
public
#if NET_2_0
- new
+ new
#endif // NET_2_0
- OdbcParameterCollection Parameters {
+ OdbcParameterCollection Parameters {
get {
#if ONLY_1_1
return _parameters;
- #else
- return base.Parameters as OdbcParameterCollection;
+#else
+ return base.Parameters as OdbcParameterCollection;
#endif // ONLY_1_1
-
}
}
[BrowsableAttribute (false)]
- [OdbcDescriptionAttribute ("The transaction used by the command")]
- [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
+ [OdbcDescriptionAttribute ("The transaction used by the command")]
+ [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Hidden)]
public
#if NET_2_0
- new
+ new
#endif // NET_2_0
- OdbcTransaction Transaction {
- get {
- return transaction;
- }
- set {
- transaction = value;
- }
+ OdbcTransaction Transaction {
+ get { return transaction; }
+ set { transaction = value; }
}
[OdbcCategory ("Behavior")]
- [DefaultValue (UpdateRowSource.Both)]
- [OdbcDescriptionAttribute ("When used by a DataAdapter.Update, how command results are applied to the current DataRow")]
- public
+ [DefaultValue (UpdateRowSource.Both)]
+ [OdbcDescriptionAttribute ("When used by a DataAdapter.Update, how command results are applied to the current DataRow")]
+ public
#if NET_2_0
override
#endif
- UpdateRowSource UpdatedRowSource {
- get {
- return updateRowSource;
- }
- set {
- updateRowSource = value;
- }
+ UpdateRowSource UpdatedRowSource {
+ get { return updateRowSource; }
+ set {
+ ExceptionHelper.CheckEnumValue (typeof (UpdateRowSource), value);
+ updateRowSource = value;
+ }
}
#if NET_2_0
- protected override DbConnection DbConnection
- {
- get { return connection; }
- set {
- connection = (OdbcConnection) value;
- }
- }
+ protected override DbConnection DbConnection {
+ get { return connection; }
+ set { connection = (OdbcConnection) value;}
+ }
#endif // NET_2_0
#if ONLY_1_1
-
IDbConnection IDbCommand.Connection {
- get {
- return Connection;
- }
- set {
- Connection = (OdbcConnection) value;
- }
+ get { return Connection; }
+ set { Connection = (OdbcConnection) value; }
}
- IDataParameterCollection IDbCommand.Parameters {
- get {
- return Parameters;
- }
+ IDataParameterCollection IDbCommand.Parameters {
+ get { return Parameters; }
+ }
+#else
+ protected override DbParameterCollection DbParameterCollection {
+ get { return _parameters as DbParameterCollection;}
}
- #else
- protected override DbParameterCollection DbParameterCollection
- {
- get { return _parameters as DbParameterCollection;}
- }
-
#endif // NET_2_0
#if ONLY_1_1
- IDbTransaction IDbCommand.Transaction {
- get {
- return (IDbTransaction) Transaction;
- }
+ IDbTransaction IDbCommand.Transaction {
+ get { return (IDbTransaction) Transaction; }
set {
- if (value is OdbcTransaction)
- {
- Transaction = (OdbcTransaction)value;
- }
- else
- {
- throw new ArgumentException ();
- }
+ if (value is OdbcTransaction) {
+ Transaction = (OdbcTransaction) value;
+ } else {
+ throw new ArgumentException ();
+ }
}
}
- #else
- protected override DbTransaction DbTransaction
- {
+#else
+ protected override DbTransaction DbTransaction {
get { return transaction; }
- set {
- transaction = (OdbcTransaction)value;
- }
+ set { transaction = (OdbcTransaction) value; }
}
#endif // ONLY_1_1
-
-
#endregion // Properties
#region Methods
public
#if NET_2_0
- override
+ override
#endif // NET_2_0
- void Cancel ()
+ void Cancel ()
{
- if (hstmt!=IntPtr.Zero)
- {
- OdbcReturn Ret=libodbc.SQLCancel(hstmt);
- if ((Ret!=OdbcReturn.Success) && (Ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLCancel",OdbcHandleType.Stmt,hstmt));
- }
- else
- throw new InvalidOperationException();
+ if (hstmt != IntPtr.Zero) {
+ OdbcReturn Ret = libodbc.SQLCancel (hstmt);
+ if (Ret != OdbcReturn.Success && Ret != OdbcReturn.SuccessWithInfo)
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
+ } else
+ throw new InvalidOperationException ();
}
#if ONLY_1_1
}
#else
- protected override DbParameter CreateDbParameter ()
- {
- return CreateParameter ();
- }
-
+ protected override DbParameter CreateDbParameter ()
+ {
+ return CreateParameter ();
+ }
#endif // ONLY_1_1
public new OdbcParameter CreateParameter ()
return new OdbcParameter ();
}
+ internal void Unlink ()
+ {
+ if (disposed)
+ return;
+
+ FreeStatement (false);
+ }
+
protected override void Dispose (bool disposing)
{
if (disposed)
return;
-
+
FreeStatement (); // free handles
+#if NET_2_0
+ CommandText = null;
+#endif
Connection = null;
Transaction = null;
+ Parameters.Clear ();
disposed = true;
}
OdbcReturn ret;
if (hstmt != IntPtr.Zero)
+ // Free the existing hstmt. Also unlinks from the connection.
FreeStatement ();
-
- ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
- if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLAllocHandle",OdbcHandleType.Dbc,Connection.hDbc));
+ // Link this command to the connection. The hstmt created below
+ // only remains valid while generation == Connection.generation.
+ generation = Connection.Link (this);
+ ret = libodbc.SQLAllocHandle (OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
+ if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+ throw connection.CreateOdbcException (OdbcHandleType.Dbc, Connection.hDbc);
disposed = false;
return hstmt;
}
- private void FreeStatement ()
+ void FreeStatement ()
+ {
+ FreeStatement (true);
+ }
+
+ private void FreeStatement (bool unlink)
{
+ prepared = false;
+
if (hstmt == IntPtr.Zero)
return;
+
+ // Normally the command is unlinked from the connection, but during
+ // OdbcConnection.Close() this would be pointless and (quadratically)
+ // slow.
+ if (unlink)
+ Connection.Unlink (this);
+
+ // Serialize with respect to the connection's own destruction
+ lock(Connection) {
+ // If the connection has already called SQLDisconnect then hstmt
+ // may have already been freed, in which case it is not safe to
+ // use. Thus the generation check.
+ if(Connection.Generation == generation) {
+ // free previously allocated handle.
+ OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
+ if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
- // free previously allocated handle.
- OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
- if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLCloseCursor",OdbcHandleType.Stmt,hstmt));
-
- ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);
- if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,hstmt));
- hstmt = IntPtr.Zero;
+ ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, hstmt);
+ if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
+ }
+ hstmt = IntPtr.Zero;
+ }
}
- private void ExecSQL(string sql)
+ private void ExecSQL (CommandBehavior behavior, bool createReader, string sql)
{
OdbcReturn ret;
- if (! prepared && Parameters.Count <= 0) {
+ if (!prepared && Parameters.Count == 0) {
ReAllocStatment ();
-
+
ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS);
- if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo) &&
- (ret != OdbcReturn.NoData))
- throw new OdbcException(new OdbcError("SQLExecDirect",OdbcHandleType.Stmt,hstmt));
+ if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo && ret != OdbcReturn.NoData)
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
return;
}
Prepare();
BindParameters ();
- ret=libodbc.SQLExecute(hstmt);
- if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLExecute",OdbcHandleType.Stmt,hstmt));
+ ret = libodbc.SQLExecute (hstmt);
+ if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
}
internal void FreeIfNotPrepared ()
public
#if NET_2_0
- override
+ override
#endif // NET_2_0
- int ExecuteNonQuery ()
+ int ExecuteNonQuery ()
{
- return ExecuteNonQuery (true);
+ return ExecuteNonQuery ("ExecuteNonQuery", CommandBehavior.Default, false);
}
- private int ExecuteNonQuery (bool freeHandle)
+ private int ExecuteNonQuery (string method, CommandBehavior behavior, bool createReader)
{
int records = 0;
if (Connection == null)
- throw new InvalidOperationException ("No open connection");
+ throw new InvalidOperationException (string.Format (
+ "{0}: Connection is not set.", method));
if (Connection.State == ConnectionState.Closed)
- throw new InvalidOperationException ("Connection state is closed");
- // FIXME: a third check is mentioned in .NET docs
+ throw new InvalidOperationException (string.Format (
+ "{0}: Connection state is closed", method));
+ if (CommandText.Length == 0)
+ throw new InvalidOperationException (string.Format (
+ "{0}: CommandText is not set.", method));
- ExecSQL(CommandText);
+ ExecSQL (behavior, createReader, CommandText);
// .NET documentation says that except for INSERT, UPDATE and
- // DELETE where the return value is the number of rows affected
- // for the rest of the commands the return value is -1.
- if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) ||
+ // DELETE where the return value is the number of rows affected
+ // for the rest of the commands the return value is -1.
+ if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) ||
(CommandText.ToUpper().IndexOf("INSERT")!=-1) ||
(CommandText.ToUpper().IndexOf("DELETE")!=-1)) {
-
int numrows = 0;
- OdbcReturn ret = libodbc.SQLRowCount(hstmt,ref numrows);
+ libodbc.SQLRowCount (hstmt, ref numrows);
records = numrows;
- }
- else
+ } else
records = -1;
- if (freeHandle && !prepared)
+ if (!createReader && !prepared)
FreeStatement ();
return records;
public
#if NET_2_0
- override
+ override
#endif // NET_2_0
- void Prepare()
+ void Prepare()
{
ReAllocStatment ();
OdbcReturn ret;
- ret=libodbc.SQLPrepare(hstmt, CommandText, CommandText.Length);
- if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
- throw new OdbcException(new OdbcError("SQLPrepare",OdbcHandleType.Stmt,hstmt));
- prepared=true;
+ ret = libodbc.SQLPrepare(hstmt, CommandText, CommandText.Length);
+ if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
+ throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
+ prepared = true;
}
private void BindParameters ()
{
- int i=1;
- foreach (OdbcParameter p in Parameters)
- {
- p.Bind(hstmt, i);
+ int i = 1;
+ foreach (OdbcParameter p in Parameters) {
+ p.Bind (this, hstmt, i);
p.CopyValue ();
i++;
}
}
-
public
#if NET_2_0
- new
+ new
#endif // NET_2_0
- OdbcDataReader ExecuteReader ()
+ OdbcDataReader ExecuteReader ()
{
return ExecuteReader (CommandBehavior.Default);
}
{
return ExecuteReader ();
}
- #else
- protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
- {
- return ExecuteReader (behavior);
- }
-
+#else
+ protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
+ {
+ return ExecuteReader (behavior);
+ }
#endif // ONLY_1_1
public
#if NET_2_0
- new
+ new
#endif // NET_2_0
- OdbcDataReader ExecuteReader (CommandBehavior behavior)
+ OdbcDataReader ExecuteReader (CommandBehavior behavior)
+ {
+ return ExecuteReader ("ExecuteReader", behavior);
+ }
+
+ OdbcDataReader ExecuteReader (string method, CommandBehavior behavior)
{
- int recordsAffected = ExecuteNonQuery(false);
- OdbcDataReader dataReader=new OdbcDataReader(this, behavior, recordsAffected);
+ int recordsAffected = ExecuteNonQuery (method, behavior, true);
+ OdbcDataReader dataReader = new OdbcDataReader (this, behavior, recordsAffected);
return dataReader;
}
#if ONLY_1_1
- IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
+ IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
{
return ExecuteReader (behavior);
}
#endif // ONLY_1_1
- public
+ public
#if NET_2_0
override
#endif
object ExecuteScalar ()
{
object val = null;
- OdbcDataReader reader=ExecuteReader();
- try
- {
+ OdbcDataReader reader = ExecuteReader ("ExecuteScalar",
+ CommandBehavior.Default);
+ try {
if (reader.Read ())
- val=reader[0];
- }
- finally
- {
- reader.Close();
+ val = reader [0];
+ } finally {
+ reader.Close ();
}
return val;
}
- [MonoTODO]
object ICloneable.Clone ()
{
- throw new NotImplementedException ();
+ OdbcCommand command = new OdbcCommand ();
+ command.CommandText = this.CommandText;
+ command.CommandTimeout = this.CommandTimeout;
+ command.CommandType = this.CommandType;
+ command.Connection = this.Connection;
+ command.DesignTimeVisible = this.DesignTimeVisible;
+ foreach (OdbcParameter parameter in this.Parameters)
+ command.Parameters.Add (parameter);
+ command.Transaction = this.Transaction;
+ return command;
}
public void ResetCommandTimeout ()
{
- CommandTimeout = 30;
+ CommandTimeout = DEFAULT_COMMAND_TIMEOUT;
}
#endregion