X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Data%2FSystem.Data.Odbc%2FOdbcCommand.cs;h=ca4f4905c9d161ec62b4401fd101d3cef933e61a;hb=8d3b80d4fb899d1892a2942b85a70c6b99e05e2b;hp=0702dc7d1a3e8c6c92eed8484c8303063fc528e4;hpb=9975a45f43793e34318e593ddacd3f332f8d91c0;p=mono.git diff --git a/mcs/class/System.Data/System.Data.Odbc/OdbcCommand.cs b/mcs/class/System.Data/System.Data.Odbc/OdbcCommand.cs index 0702dc7d1a3..ca4f4905c9d 100644 --- a/mcs/class/System.Data/System.Data.Odbc/OdbcCommand.cs +++ b/mcs/class/System.Data/System.Data.Odbc/OdbcCommand.cs @@ -39,12 +39,13 @@ using System.Runtime.InteropServices; namespace System.Data.Odbc { - /// + /// /// Represents an SQL statement or stored procedure to execute against a data source. /// [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 @@ -52,20 +53,23 @@ namespace System.Data.Odbc { #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 @@ -73,21 +77,16 @@ namespace System.Data.Odbc 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) @@ -96,8 +95,7 @@ namespace System.Data.Odbc Connection = connection; } - public OdbcCommand (string cmdText, - OdbcConnection connection, + public OdbcCommand (string cmdText, OdbcConnection connection, OdbcTransaction transaction) : this (cmdText, connection) { this.Transaction = transaction; @@ -107,228 +105,197 @@ namespace System.Data.Odbc #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")] - [DefaultValue (30)] - 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 - 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)] - public + [DesignOnlyAttribute (true)] + [DefaultValue (true)] +#if NET_2_0 + [EditorBrowsable (EditorBrowsableState.Never)] +#endif + 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 { - [MonoTODO] - get { - return updateRowSource; - } - [MonoTODO] - 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 @@ -338,11 +305,10 @@ namespace System.Data.Odbc } #else - protected override DbParameter CreateDbParameter () - { - return CreateParameter (); - } - + protected override DbParameter CreateDbParameter () + { + return CreateParameter (); + } #endif // ONLY_1_1 public new OdbcParameter CreateParameter () @@ -350,14 +316,26 @@ namespace System.Data.Odbc 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; } @@ -366,41 +344,65 @@ namespace System.Data.Odbc 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, sql.Length); - if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) - throw new OdbcException(new OdbcError("SQLExecDirect",OdbcHandleType.Stmt,hstmt)); + + ret = libodbc.SQLExecDirect (hstmt, sql, libodbc.SQL_NTS); + if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo && ret != OdbcReturn.NoData) + throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt); return; } @@ -408,9 +410,9 @@ namespace System.Data.Odbc 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 () @@ -421,39 +423,41 @@ namespace System.Data.Odbc 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 (); + throw new InvalidOperationException (string.Format ( + "{0}: Connection is not set.", method)); if (Connection.State == ConnectionState.Closed) - throw new InvalidOperationException (); - // 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; @@ -461,36 +465,34 @@ namespace System.Data.Odbc 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); } @@ -500,61 +502,71 @@ namespace System.Data.Odbc { 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