#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#60541 | Julio casal | 1 Apr |