List:Commits« Previous MessageNext Message »
From:Julio casal Date:April 1 2011 4:33pm
Subject:bzr commit into connector-net-6.1 branch (julio.casal:879) Bug#60541
View as plain text  
#At file:///C:/Users/jcasalt/Dev/connector-net/6.1/ based on revid:reggie.burnett@stripped

  879 Julio casal	2011-04-01
      Introduces workaround to unexpected query aborts (server 5.1+) when executing a datareader after a command.Cancel() (MySQL bug#60541).

    modified:
      MySql.Data/Provider/Source/Connection.cs
      MySql.Data/Provider/Source/command.cs
      MySql.Data/Provider/Source/datareader.cs
      MySql.Data/Tests/Source/CommandTests.cs
=== modified file 'MySql.Data/Provider/Source/Connection.cs'
=== modified file 'MySql.Data/Provider/Source/Connection.cs'
--- a/MySql.Data/Provider/Source/Connection.cs	2010-08-18 19:03:33 +0000
+++ b/MySql.Data/Provider/Source/Connection.cs	2011-04-01 16:33:54 +0000
@@ -53,6 +53,7 @@
         private bool hasBeenOpen;
         private SchemaProvider schemaProvider;
         private ProcedureCache procedureCache;
+        private bool isInUse;
 #if !CF
         private PerformanceMonitor perfMonitor;
 #endif
@@ -104,7 +105,11 @@
         internal MySqlDataReader Reader
         {
             get { return dataReader; }
-            set { dataReader = value; }
+            set 
+            { 
+                dataReader = value;
+                isInUse = true;
+            }
         }
 
         internal void OnInfoMessage(MySqlInfoMessageEventArgs args)
@@ -135,6 +140,12 @@
             }
         }
 
+        internal bool IsInUse
+        {
+            get{ return isInUse; }
+            set{ isInUse = value; }
+        }
+
         #endregion
 
         #region Properties

=== modified file 'MySql.Data/Provider/Source/command.cs'
--- a/MySql.Data/Provider/Source/command.cs	2011-03-09 18:15:07 +0000
+++ b/MySql.Data/Provider/Source/command.cs	2011-04-01 16:33:54 +0000
@@ -60,6 +60,7 @@
         List<MySqlCommand> batch;
         private string batchableCommandText;
         private bool useDefaultTimeout;
+        private bool internallyCreated;
 
 		/// <include file='docs/mysqlcommand.xml' path='docs/ctor1/*'/>
 		public MySqlCommand()
@@ -237,11 +238,22 @@
             get { return timedOut; }
         }
 
+        internal bool Canceled
+        {
+            get { return canceled; }
+        }
+
         internal string BatchableCommandText
         {
             get { return batchableCommandText; }
         }
 
+        internal bool InternallyCreated
+        {
+            get { return internallyCreated; }
+            set { internallyCreated = value; }
+        }
+
 		#endregion
 
 		#region Methods
@@ -301,7 +313,7 @@
                 throw new InvalidOperationException("Connection must be valid and open.");
 
 			// Data readers have to be closed first
-			if (connection.Reader != null)
+            if (connection.IsInUse && !this.internallyCreated)
 				throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first.");
 
             if (CommandType == CommandType.TableDirect)
@@ -327,6 +339,7 @@
 		{
 			if (statement != null)
 				statement.Close(reader);
+
             ResetSqlSelectLimit();
         }
 
@@ -459,14 +472,14 @@
                 }
                 catch (Exception) { }
 
-
                 // if we caught an exception because of a cancel, then just return null
                 if (ex.IsQueryAborted)
                 {
                     if (TimedOut)
                         throw new MySqlException(Resources.Timeout);
                     return null;
-                }
+                } 
+
                 if (ex.IsFatal)
                     Connection.Close();
                 if (ex.Number == 0)

=== modified file 'MySql.Data/Provider/Source/datareader.cs'
--- a/MySql.Data/Provider/Source/datareader.cs	2010-12-14 15:18:14 +0000
+++ b/MySql.Data/Provider/Source/datareader.cs	2011-04-01 16:33:54 +0000
@@ -29,6 +29,7 @@
 using System.Collections.Generic;
 using System.Globalization;
 using MySql.Data.MySqlClient.Properties;
+using MySql.Data.Common;
 
 namespace MySql.Data.MySqlClient
 {
@@ -185,7 +186,7 @@
 
 			bool shouldCloseConnection = (commandBehavior & CommandBehavior.CloseConnection) != 0;
 			commandBehavior = CommandBehavior.Default;
-			connection.Reader = null;
+			connection.Reader = null;            
 
             // clear all remaining resultsets
             resultSet.ClearAll();
@@ -194,10 +195,17 @@
 			// stored procedures it needs to update out and inout parameters
 			command.Close(this);
 
+            if (this.command.Canceled && connection.driver.Version.isAtLeast(5, 1, 0))
+            {
+                // Issue dummy command to clear kill flag
+                ClearKillFlag();
+            }
+
 			if (shouldCloseConnection)
 				connection.Close();
 
 			command = null;
+            connection.IsInUse = false;
 			connection = null;
 
 			isOpen = false;
@@ -927,6 +935,16 @@
 			return v;
 		}
 
+
+        private void ClearKillFlag()
+        {
+            // This query will silently crash because of the Kill call that happened before.
+            string dummyStatement = string.Format("SELECT table_name FROM information_schema.tables");
+            MySqlCommand dummyCommand = new MySqlCommand(dummyStatement, connection);
+            dummyCommand.InternallyCreated = true;
+            IDataReader reader = dummyCommand.ExecuteReader(); // ExecuteReader catches the exception and returns null, which is expected.
+        }
+
 		#region IEnumerator
 
 		/// <summary>

=== modified file 'MySql.Data/Tests/Source/CommandTests.cs'
--- a/MySql.Data/Tests/Source/CommandTests.cs	2011-03-22 19:44:59 +0000
+++ b/MySql.Data/Tests/Source/CommandTests.cs	2011-04-01 16:33:54 +0000
@@ -530,6 +530,35 @@
 
             Assert.AreEqual(1, listener.Find("UPDATE"));
         }
+
+        [Test]
+        public void ExecuteReaderReturnsReaderAfterCancel()
+        {
+            execSQL("DROP TABLE IF EXISTS TableWithDateAsPrimaryKey");
+            execSQL("DROP TABLE IF EXISTS TableWithStringAsPrimaryKey");
+            createTable("CREATE TABLE TableWithDateAsPrimaryKey(PrimaryKey date NOT NULL, PRIMARY KEY  (PrimaryKey))", "InnoDB");
+            createTable("CREATE TABLE TableWithStringAsPrimaryKey(PrimaryKey nvarchar(50) NOT NULL, PRIMARY KEY  (PrimaryKey))", "InnoDB");
+
+            string connStr = GetConnectionString(true);
+            using (MySqlConnection connection = new MySqlConnection(connStr))
+            {
+                connection.Open();
+                MySqlCommand command = new MySqlCommand("SELECT PrimaryKey FROM TableWithDateAsPrimaryKey", connection);
+                IDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo);
+                DataTable dataTableSchema = reader.GetSchemaTable();
+                command.Cancel();
+                reader.Close();
+
+                command = new MySqlCommand("SELECT PrimaryKey FROM TableWithStringAsPrimaryKey", connection);
+                reader = command.ExecuteReader(CommandBehavior.KeyInfo);
+                Assert.IsNotNull(reader);
+
+                dataTableSchema = reader.GetSchemaTable();
+                Assert.AreEqual("PrimaryKey", dataTableSchema.Rows[0][dataTableSchema.Columns[0]]);
+                
+                reader.Close();
+            }
+        }
 #endif
     }
 


Attachment: [text/bzr-bundle] bzr/julio.casal@oracle.com-20110401163354-mg7c90gk1ve7y40t.bundle
Thread
bzr commit into connector-net-6.1 branch (julio.casal:879) Bug#60541Julio casal1 Apr