* OdbcCommandTest.cs: Added test for bug #341743.
authorGert Driesen <drieseng@users.sourceforge.net>
Tue, 30 Dec 2008 04:33:40 +0000 (04:33 -0000)
committerGert Driesen <drieseng@users.sourceforge.net>
Tue, 30 Dec 2008 04:33:40 +0000 (04:33 -0000)
* OdbcColumn.cs: Treat OdbcType.Numeric as decimal.
* OdbcConnection.cs: Keep (weak) list of commands to allow us to
dispose them (and free their corresponding statement handles) when
the connection is closed. Fixed bug #341743.
* OdbcCommand.cs: Add internal Unlink method that is invoked when the
OdbcConnection is disposed, and which frees the statement handle.
Modified ExecuteNonQuery helper to take CommandBehavior and rename
freeHandle argument to createReader. Added CommandBehavior argument
to ExecSQL. Added FreeStatement overload that takes an option to not
perform an unlink. Fixes bug #3417434.

svn path=/trunk/mcs/; revision=122226

mcs/class/System.Data/System.Data.Odbc/ChangeLog
mcs/class/System.Data/System.Data.Odbc/OdbcColumn.cs
mcs/class/System.Data/System.Data.Odbc/OdbcCommand.cs
mcs/class/System.Data/System.Data.Odbc/OdbcConnection.cs
mcs/class/System.Data/Test/ProviderTests/System.Data.Odbc/ChangeLog
mcs/class/System.Data/Test/ProviderTests/System.Data.Odbc/OdbcCommandTest.cs

index fc6a74c638ef257d3ee50e9787f133b4706705ee..e628b4afd6606c26276b192ee52c722437c331dc 100644 (file)
@@ -1,3 +1,16 @@
+2008-12-30  Gert Driesen  <drieseng@users.sourceforge.net>
+
+       * OdbcColumn.cs: Treat OdbcType.Numeric as decimal.
+       * OdbcConnection.cs: Keep (weak) list of commands to allow us to
+       dispose them (and free their corresponding statement handles) when
+       the connection is closed. Fixed bug #341743.
+       * OdbcCommand.cs: Add internal Unlink method that is invoked when the
+       OdbcConnection is disposed, and which frees the statement handle.
+       Modified ExecuteNonQuery helper to take CommandBehavior and rename
+       freeHandle argument to createReader. Added CommandBehavior argument
+       to ExecSQL. Added FreeStatement overload that takes an option to not
+       perform an unlink. Fixes bug #3417434.
+
 2008-12-30  Gert Driesen  <drieseng@users.sourceforge.net>
 
        * libodbc.cs: Add IdentifierQuoteChar to OdbcInfo.
index 4b4f32ffe8159c5980f92f78cb8b95792f4921ad..a7160d823e983d4ceef7cb7238d3c3fc40fe4196 100644 (file)
@@ -88,8 +88,8 @@ namespace System.Data.Odbc
                                        case OdbcType.SmallDateTime:
                                                return typeof (DateTime);
                                        case OdbcType.Decimal:
-                                               return typeof (Decimal);
                                        case OdbcType.Numeric:
+                                               return typeof (Decimal);
                                        case OdbcType.Double:
                                                return typeof (Double);
                                        case OdbcType.Int:
index 9ccb4143b5868fd225c0e4df7910bbff52ac4372..edf4bb08f7cd7f0e6584240d5df708aaf68f80f7 100644 (file)
@@ -107,7 +107,6 @@ namespace System.Data.Odbc
                internal IntPtr hStmt {
                        get { return hstmt; }
                }
-               
 
                [OdbcCategory ("Data")]
                [DefaultValue ("")]
@@ -125,7 +124,9 @@ namespace System.Data.Odbc
                                return commandText;
                        }
                        set {
+#if NET_2_0
                                prepared = false;
+#endif
                                commandText = value;
                        }
                }
@@ -199,7 +200,6 @@ namespace System.Data.Odbc
                        }
                }
 
-
                [OdbcCategory ("Data")]
                [OdbcDescriptionAttribute ("The parameters collection")]
                [DesignerSerializationVisibilityAttribute (DesignerSerializationVisibility.Content)]
@@ -291,7 +291,7 @@ namespace System.Data.Odbc
                                }
                        }
                }
-               #else
+#else
                protected override DbTransaction DbTransaction {
                        get { return transaction; }
                        set { transaction = (OdbcTransaction) value; }
@@ -334,11 +334,19 @@ 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
                        Connection = null;
                        Transaction = null;
@@ -351,6 +359,8 @@ namespace System.Data.Odbc
 
                        if (hstmt != IntPtr.Zero)
                                FreeStatement ();
+                       else
+                               Connection.Link (this);
 
                        ret = libodbc.SQLAllocHandle (OdbcHandleType.Stmt, Connection.hDbc, ref hstmt);
                        if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
@@ -359,11 +369,21 @@ namespace System.Data.Odbc
                        return hstmt;
                }
 
-               private void FreeStatement ()
+               void FreeStatement ()
+               {
+                       FreeStatement (true);
+               }
+
+               private void FreeStatement (bool unlink)
                {
+                       prepared = false;
+
                        if (hstmt == IntPtr.Zero)
                                return;
-                       
+
+                       if (unlink)
+                               Connection.Unlink (this);
+
                        // free previously allocated handle.
                        OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
                        if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
@@ -375,16 +395,15 @@ namespace System.Data.Odbc
                        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))
+                               if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo) && (ret != OdbcReturn.NoData))
                                        throw connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
                                return;
                        }
@@ -410,10 +429,10 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                int ExecuteNonQuery ()
                {
-                       return ExecuteNonQuery (true);
+                       return ExecuteNonQuery (CommandBehavior.Default, false);
                }
 
-               private int ExecuteNonQuery (bool freeHandle) 
+               private int ExecuteNonQuery (CommandBehavior behavior, bool createReader)
                {
                        int records = 0;
                        if (Connection == null)
@@ -421,8 +440,7 @@ namespace System.Data.Odbc
                        if (Connection.State == ConnectionState.Closed)
                                throw new InvalidOperationException ("Connection state is closed");
                        // FIXME: a third check is mentioned in .NET docs
-
-                       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
@@ -436,7 +454,7 @@ namespace System.Data.Odbc
                        } else
                                records = -1;
 
-                       if (freeHandle && !prepared)
+                       if (!createReader && !prepared)
                                FreeStatement ();
                        
                        return records;
@@ -494,7 +512,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                OdbcDataReader ExecuteReader (CommandBehavior behavior)
                {
-                       int recordsAffected = ExecuteNonQuery(false);
+                       int recordsAffected = ExecuteNonQuery(behavior, true);
                        OdbcDataReader dataReader = new OdbcDataReader (this, behavior, recordsAffected);
                        return dataReader;
                }
index d1ce0136ae03e7de5910be3ae6afd87bced6b420..57978f839a170c97848dd6fcd1467357057517ce 100644 (file)
@@ -29,6 +29,8 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
+
+using System.Collections;
 using System.ComponentModel;
 using System.Data;
 using System.Data.Common;
@@ -56,6 +58,7 @@ namespace System.Data.Odbc
                IntPtr henv = IntPtr.Zero;
                IntPtr hdbc = IntPtr.Zero;
                bool disposed;
+               ArrayList linkedCommands;
 
                #endregion
 
@@ -258,6 +261,19 @@ namespace System.Data.Odbc
                {
                        OdbcReturn ret = OdbcReturn.Error;
                        if (State == ConnectionState.Open) {
+                               // close any associated commands
+                               if (linkedCommands != null) {
+                                       for (int i = 0; i < linkedCommands.Count; i++) {
+                                               WeakReference wr = (WeakReference) linkedCommands [i];
+                                               if (wr == null)
+                                                       continue;
+                                               OdbcCommand c = (OdbcCommand) wr.Target;
+                                               if (c != null)
+                                                       c.Unlink ();
+                                       }
+                                       linkedCommands = null;
+                               }
+
                                // disconnect
                                ret = libodbc.SQLDisconnect (hdbc);
                                if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
@@ -564,6 +580,30 @@ namespace System.Data.Odbc
                        return value.TrimEnd ('\0');
                }
 
+               internal void Link (OdbcCommand cmd)
+               {
+                       if (linkedCommands == null)
+                               linkedCommands = new ArrayList ();
+                       linkedCommands.Add (new WeakReference (cmd));
+               }
+
+               internal void Unlink (OdbcCommand cmd)
+               {
+                       if (linkedCommands == null)
+                               return;
+
+                       for (int i = 0; i < linkedCommands.Count; i++) {
+                               WeakReference wr = (WeakReference) linkedCommands [i];
+                               if (wr == null)
+                                       continue;
+                               OdbcCommand c = (OdbcCommand) wr.Target;
+                               if (c == cmd) {
+                                       linkedCommands [i] = null;
+                                       break;
+                               }
+                       }
+               }
+
                #endregion
 
                #region Events and Delegates
index 6fe7734d12fd0d0fd210396e2f3677df39c6fedd..a0ca4076165074663dbe5ed688c128be33e8b2d0 100644 (file)
@@ -1,3 +1,7 @@
+2008-12-30  Gert Driesen  <drieseng@users.sourceforge.net>
+
+       * OdbcCommandTest.cs: Added test for bug #341743.
+
 2008-07-28  Gert Driesen  <drieseng@users.sourceforge.net>
 
        * OdbcParameterTest.cs: Fixed compilation on 1.0 profile.
index e3d25566ea90bc6a0d5a84874cfafcd167869504..d5370e44a9b642f9a700baf49efc280838c2e7b0 100644 (file)
@@ -113,6 +113,30 @@ namespace MonoTests.System.Data
                        }
                }
 
+               [Test]
+               public void bug341743 ()
+               {
+                       OdbcConnection conn = (OdbcConnection) ConnectionManager.Singleton.Connection;
+                       ConnectionManager.Singleton.OpenConnection ();
+
+                       OdbcCommand cmd = null;
+                       try {
+                               cmd = conn.CreateCommand ();
+                               cmd.CommandText = "SELECT 'a'";
+                               cmd.ExecuteNonQuery ();
+
+                               conn.Dispose ();
+
+                               Assert.AreSame (conn, cmd.Connection, "#1");
+                               cmd.Dispose ();
+                               Assert.IsNull (cmd.Connection, "#2");
+                       } finally {
+                               if (cmd != null)
+                                       cmd.Dispose ();
+                               ConnectionManager.Singleton.CloseConnection ();
+                       }
+               }
+
                /// <summary>
                 /// Test ExecuteNonQuery
                 /// </summary>