Bug #44960 backslash in string - connector return exeption
Submitted: 19 May 2009 18:46 Modified: 26 May 2009 13:18
Reporter: Moshe Lampert Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S3 (Non-critical)
Version:6.0.3 OS:Any
Assigned to: Vladislav Vaintroub CPU Architecture:Any
Tags: regression

[19 May 2009 18:46] Moshe Lampert
Description:
SQL string with escaped backslash inside.

examples:

insert into pb_im set m_from=1, m_to=1, m_content='\\=';
insert into pb_im set m_from=1, m_to=1, m_content='\\'; /* works sometimes, fail somtimes */

works on Query Browser/CLI/connector 5.x, fail on 6.0.3:

Index and length must refer to a location within the string. 
Parameter name: length 
at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean fAlwaysCopy) 
at MySql.Data.MySqlClient.MySqlTokenizer.NextParameter() 
at MySql.Data.MySqlClient.Statement.InternalBindParameters(String sql, MySqlParameterCollection parameters, MySqlPacket packet) 
at MySql.Data.MySqlClient.Statement.BindParameters() 
at MySql.Data.MySqlClient.PreparableStatement.Execute() 
at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior be‎havior) 
at MySql.Data.MySqlClient.MySqlCommand.ExecuteNonQuery() / Scalar / etc

How to repeat:
open connection, end execute SQL with "=\" escaped and sometimes text and "\" (escaped = "\\") in the end of the string

insert into pb_im set m_from=1, m_to=1, m_content='\\=';
insert into pb_im set m_from=1, m_to=1, m_content='ב6\\';
insert into pb_im set m_from=1, m_to=1, m_content='ב..\\';
[20 May 2009 8:04] Tonci Grgin
Hi Moshe and thanks for your report.

I would like to see compact but complete test case attached as well as MySQL server info and my.ini file.
[20 May 2009 8:36] Tonci Grgin
Bug#44973 was marked as duplicate of this report.
[20 May 2009 8:43] Moshe Lampert
MySQL Version: 5.1.30-2 (@debian)
Connector Version: 6.0.3
MySQL Encoding: utf-8

Using Conn As New MySqlConnection(...)
	Conn.Open()
	Dim Cmd As new MySQLCommand("",Conn)
	Cmd.CommandText="insert into pb_im set m_from=1, m_to=1, m_content='\\=';"
	Cmd.ExecuteNonQuery()

End Using ' and close/dispose the connection
[20 May 2009 8:47] Tonci Grgin
Moshe, please please attach *full* test case. Not only am I missing DDL/DML statements but also a connection string...
[20 May 2009 10:44] Moshe Lampert
connstring:
server=*;user id=*; password=*; database=*; pooling=true; Min Pool Size=40;Max Pool Size=150; Connect Timeout=5;default command timeout=10

There is no other code, as my testcase is simple SQL queries (in this code - some small ASHX code).
[20 May 2009 12:17] Tonci Grgin
Ok, this test case:
      MySqlConnection con = new MySqlConnection();
      con.ConnectionString = "DataSource=**;Database=test;UserID=**;Password=**;PORT=**;logging=True;";
      con.Open();
      MySqlCommand cmdCreateTable = new MySqlCommand("DROP TABLE IF EXISTS bug44973", con);
      cmdCreateTable.ExecuteNonQuery();
      cmdCreateTable.CommandText = "CREATE TABLE `bug44973`(`ID` int UNSIGNED NOT NULL auto_increment, `file_name` varchar(256) COLLATE latin1_bin NOT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin;";
      cmdCreateTable.ExecuteNonQuery();

      string query = "INSERT INTO bug44973 VALUES (NULL, '2bsl=\\-1bsl=\')";
      cmdCreateTable.CommandText = query;
      Assert.AreEqual(1, cmdCreateTable.ExecuteNonQuery());
      cmdCreateTable.Dispose();
      con.Close();
      con.Dispose();

produces following output:
[20.5.2009 14:10:45] - Executing command QUERY with text ='SHOW VARIABLES'
[20.5.2009 14:10:45] - Executing command QUERY with text ='SHOW COLLATION'
[20.5.2009 14:10:45] - Executing command QUERY with text ='SET NAMES utf8;SET character_set_results=NULL'
[20.5.2009 14:10:46] - Executing command QUERY with text ='DROP TABLE IF EXISTS bug44973'
[20.5.2009 14:10:46] - Executing command QUERY with text ='CREATE TABLE `bug44973`(`ID` int UNSIGNED NOT NULL auto_increment, `file_name` varchar(256) COLLATE latin1_bin NOT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin'
[20.5.2009 14:10:46] - Executing command QUERY with text ='INSERT INTO bug44973 VALUES (NULL, '2bsl=\-1bsl=')' <<<<

So, I did not get any crashes but what I see appears as regression from Bug#4505 to me. Adding reference to Bug#44973 too.
[20 May 2009 12:55] Alexander Sosedkin
Look for the string that you want to insert '2bsl=\\-1bsl=\'
and a string in the output '2bsl=\-1bsl=', I think you can see the difference?

Or change in the your test сase, for insert a string '\\', which should
insert a backslash, you get an exception:
System.ArgumentOutOfRangeException: Index and length must refer to a location within the
string. Parameter name: length
[20 May 2009 13:05] Tonci Grgin
Alexander, of course I see :) And as for later part (inserting '\\') I made reference to your bug.
[20 May 2009 13:11] Alexander Sosedkin
OK :)
Thank you!
[21 May 2009 8:05] Alexander Sosedkin
I rushed to the conclusion, then that variable is "escaped" is not needed.
In my opinion code "MySqlTokenizer.ReadQuotedToken" should look like:

private void ReadQuotedToken(char quoteChar)
{
    startIndex = pos-1;
    bool escaped = false;

    while (pos < sql.Length)
    {
        char c = sql[pos];

        if (escaped)
            escaped = false;
        else if (c == quoteChar)
            break;
        else if (c == '\\' && BackslashEscapes)
            escaped = true;
            
        pos++;
    }
    
    pos++;
    Quoted = true;
    stopIndex = pos;
}

If this can help you.
[21 May 2009 8:13] Tonci Grgin
Alexander, thanks for your contribution.
[22 May 2009 21:45] 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/74815
[22 May 2009 21:46] Reggie Burnett
fixed in 6.0.4
[26 May 2009 13:18] Tony Bedford
An entry was added to the 6.0.4 changelog:

A SQL query string containing an escaped backslash caused an exception to be generated:

Index and length must refer to a location within the string. 
Parameter name: length 
at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean
fAlwaysCopy) 
at MySql.Data.MySqlClient.MySqlTokenizer.NextParameter() 
at MySql.Data.MySqlClient.Statement.InternalBindParameters(String sql,
MySqlParameterCollection parameters, MySqlPacket packet) 
at MySql.Data.MySqlClient.Statement.BindParameters() 
at MySql.Data.MySqlClient.PreparableStatement.Execute() 
at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior be‎havior) 
at MySql.Data.MySqlClient.MySqlCommand.ExecuteNonQuery()
[5 Aug 2009 4:29] Dennis Haney
It seems the actual bug wasnt really fixed...

public string NextParameter()
    {
        while (this.FindToken())
        {
            if ((this.stopIndex - this.startIndex) >= 2)
            {
                this.sql.Substring(this.startIndex, this.stopIndex - this.startIndex).Trim();                  <----- what on earth does this line do :D
                char ch = this.sql[this.startIndex];
                char ch2 = this.sql[this.startIndex + 1];
                if ((ch == '?') || ((ch == '@') && (ch2 != '@')))
                {
                    return this.sql.Substring(this.startIndex, this.stopIndex - this.startIndex);
                }
            }
        }
        return null;
    }