Bug #37955 Connector/NET keeps adding the same option to the connection string
Submitted: 8 Jul 2008 5:57 Modified: 9 Jul 2008 8:22
Reporter: Bogdan Degtyariov Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S2 (Serious)
Version:5.2.2 OS:Windows
Assigned to: Bogdan Degtyariov CPU Architecture:Any
Tags: Allow User Variables

[8 Jul 2008 5:57] Bogdan Degtyariov
Description:
In a .NET application MySQL Connector/NET modifies the connection string so that it contains several occurrences of the same option with different values. For instance: 

The original connection string:
host=localhost;database=test;uid=*****;pwd=*****;connect
timeout=25;auto enlist=false;pooling=false;

The connection string after after closing MySqlDataReader:
host=localhost;database=test;uid=*****;pwd=*****;connect
timeout=25;auto enlist=false;pooling=false;Allow User
Variables=True;Allow User Variables=False;Allow User
Variables=True;Allow User Variables=False;

How to repeat:
Create a test table as follows:

DROP TABLE IF EXISTS `test`.`tab_bug_connstr`;
CREATE TABLE  `test`.`tab_bug_connstr` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `c1` int(10) unsigned NOT NULL,
  `c2` datetime default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `test`.`tab_bug_connst` VALUES (1, 555, '2008-07-01 16:59:04');

Create a stored procedure as follows:

DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`proc_bug_connstr` $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_bug_connstr`(INOUT param1 DATETIME, INOUT param2 DATETIME)
BEGIN
     SELECT * FROM tab_bug_connstr where c2>param1;
     SET param1 = NOW();
     SET param2 = NOW();
END $$

DELIMITER ;

Please note that the stored procedure has to return result as select and modify the INOUT parameters.

C# test case:

string cs = "host=localhost;database=test;uid=*****;pwd=*****;connect timeout=25;auto enlist=false;pooling=false;";

MySqlConnection c1 = new MySqlConnection(cs);
c1.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = c1;

cmd.CommandText = "proc_bug_connstr";
cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.AddWithValue("param1", (DateTime)new DateTime(2000, 01, 01));
cmd.Parameters["?param1"].Direction = ParameterDirection.InputOutput;

cmd.Parameters.AddWithValue("param2", (DateTime)new DateTime(2000, 01, 01));
cmd.Parameters["?param2"].Direction = ParameterDirection.InputOutput;

MySqlDataReader dr = cmd.ExecuteReader();
Console.WriteLine("Results 1:");
while (dr.Read())
{
   Console.WriteLine(dr["c1"].ToString() + " " +dr["c2"].ToString());
}

Console.WriteLine("Connection string 1.0: " + c1.ConnectionString);
dr.Close();
Console.WriteLine("Connection string 1.1: " + c1.ConnectionString);

Console.WriteLine("-----------------------------");

/* Do the same again */
dr = cmd.ExecuteReader();
Console.WriteLine("Results 2:");
while (dr.Read())
{
   Console.WriteLine(dr["c1"].ToString() + " " +dr["c2"].ToString());
}

Console.WriteLine("Connection string 2.0: " + c1.ConnectionString);
dr.Close();
Console.WriteLine("Connection string 2.1: " + c1.ConnectionString);

Console.WriteLine("-----------------------------");

c1.Close();

------------------------------
Test case output:
Results 1:
555 01.07.2008 16:59:04
Connection string 1.0: host=localhost;database=test;uid=****;connect timeout=25;auto enlist=false;pooling=false
Connection string 1.1: host=localhost;database=test;uid=****;connect timeout=25;auto enlist=false;pooling=false;Allow User Variables=True;Allow User Variables=False;
-----------------------------
Results 2:
555 01.07.2008 16:59:04
Connection string 2.0: host=localhost;database=test;uid=****;connect timeout=25;auto enlist=false;pooling=false;Allow User Variables=True;Allow User Variables=False;
Connection string 2.1: host=localhost;database=test;uid=****;connect timeout=25;auto enlist=false;pooling=false;Allow User Variables=True;Allow User Variables=False;Allow User Variables=True;Allow User Variables=False;
-----------------------------

Suggested fix:
Looks like when closing the data reader Connector/NET enables and restores the previous value of Allow User Variables parameter. Therefore it is essential to remove previous occurrences of the parameter from the connection string like below:

Index: MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs
===================================================================
--- MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	(revision 1327)
+++ MySql.Data/Provider/Source/MySqlConnectionStringBuilder.cs	(working copy)
@@ -1228,11 +1228,18 @@
         {
             if (value == null)
                 throw new ArgumentException(Resources.KeywordNoNull, keyword);
+            object out_obj;
+            TryGetValue(keyword, out out_obj);
             Keyword kw = GetKey(keyword);
             SetValue(kw, value);
             base[keyword] = value;
             if (kw != Keyword.Password)
+            {
+                /* Nothing bad happens if the substring is not found */
+                persistConnString.Replace(keyword + "=" + out_obj.ToString() + ";", "");
+
                 persistConnString.AppendFormat(CultureInfo.InvariantCulture, "{0}={1};", keyword, value);
+            }
         }
 
         private void SetValue(Keyword kw, object value)
[8 Jul 2008 18:06] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/49228
[8 Jul 2008 18:10] Reggie Burnett
Fixed MySqlConnectionStringBuilder to first remove old keyword settings when setting a value that was previously set.  This is fixed in 5.0.10, 5.1.7, and 5.2.3
[9 Jul 2008 8:22] Tony Bedford
An entry has been added to the 5.0.10, 5.1.7 and 5.2.3 Changelogs:

In a .NET application MySQL Connector/NET modifies the connection string so that it contains several occurrences of the same option with different values. This is illustrated by the example that follows. 

The original connection string: 

  host=localhost;database=test;uid=*****;pwd=*****;
  connect timeout=25; auto enlist=false;pooling=false;

The connection string after after closing MySqlDataReader: 

  host=localhost;database=test;uid=*****;pwd=*****;
  connect timeout=25;auto enlist=false;pooling=false; 
  Allow User Variables=True;Allow User Variables=False;
  Allow User Variables=True;Allow User Variables=False;