Description:
I have discovered problem with implementation of MySql.Data.MySqlClient.PreparedStatement.
In method Execute there is MySqlParameterCollection parameters parameter.
this parameter is converted into BitArray and then into byte[]
In case parameters.Count == 0 problem occurs with
nullMap.CopyTo( nullMapBytes, 0 );
nullMap is BitArray of Length = 0
and CopyTo (if implemented properly -> found in MSDN specs and in Mono too)
fails with ArgumentException as index (0) is >= Destination array length.
MS .NET (1.1, 2.0) ignores this, but Mono doesn't and my question about it was answered as: "Should we simulate this bug?"
How to repeat:
This is testing piece of code for that bug. It doesn't use MySql.Data, but it simulates the problem:
using System;
using System.Collections;
namespace BitArrTest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
ArrayList al = new ArrayList();
//al.Add(1);
//al.Add(0);
BitArray ba = new BitArray(al.Count);
for(int i = 0; i < al.Count; i++)
if((int)al[i] != 0)
ba[i] = true;
else
ba[i] = false;
byte[] bya = new byte[(al.Count+7)/8];
ba.CopyTo(bya, 0);
Console.WriteLine("Done");
}
}
}
And this is piece of code inside MySql.Data.MySqlClient.PreparedStatement:
public CommandResult Execute( MySqlParameterCollection parameters )
{
if (parameters.Count < paramList.Length)
throw new MySqlException( "Invalid number of parameters for statement execute" );
PacketWriter packet = new PacketWriter();
packet.Driver = (NativeDriver)driver;
//TODO: support long data here
// create our null bitmap
BitArray nullMap = new BitArray( parameters.Count ); //metaData.Length );
for (int x=0; x < parameters.Count; x++)
{
if (parameters[x].Value == DBNull.Value)
nullMap[x] = true;
}
byte[] nullMapBytes = new byte[ (parameters.Count + 7)/8 ];
nullMap.CopyTo( nullMapBytes, 0 );
As you can see it is almost the same.
Suggested fix:
Well, how to fix it. I suggest following update:
public CommandResult Execute( MySqlParameterCollection parameters )
{
if (parameters.Count < paramList.Length)
throw new MySqlException( "Invalid number of parameters for statement execute" );
PacketWriter packet = new PacketWriter();
packet.Driver = (NativeDriver)driver;
//TODO: support long data here
// create our null bitmap
BitArray nullMap = new BitArray( parameters.Count ); //metaData.Length );
for (int x=0; x < parameters.Count; x++)
{
if (parameters[x].Value == DBNull.Value)
nullMap[x] = true;
}
byte[] nullMapBytes = new byte[ (parameters.Count + 7)/8 ];
if(nullMapBytes.Length > 0) // This fix problem with ArgumentException and nullMapBytes is already created and in case of length=0 there is no data to copy...
nullMap.CopyTo( nullMapBytes, 0 );
Very simple and now it follows .NET specifications and works with Mono.
Description: I have discovered problem with implementation of MySql.Data.MySqlClient.PreparedStatement. In method Execute there is MySqlParameterCollection parameters parameter. this parameter is converted into BitArray and then into byte[] In case parameters.Count == 0 problem occurs with nullMap.CopyTo( nullMapBytes, 0 ); nullMap is BitArray of Length = 0 and CopyTo (if implemented properly -> found in MSDN specs and in Mono too) fails with ArgumentException as index (0) is >= Destination array length. MS .NET (1.1, 2.0) ignores this, but Mono doesn't and my question about it was answered as: "Should we simulate this bug?" How to repeat: This is testing piece of code for that bug. It doesn't use MySql.Data, but it simulates the problem: using System; using System.Collections; namespace BitArrTest { /// <summary> /// Summary description for Class1. /// </summary> class Class1 { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main(string[] args) { ArrayList al = new ArrayList(); //al.Add(1); //al.Add(0); BitArray ba = new BitArray(al.Count); for(int i = 0; i < al.Count; i++) if((int)al[i] != 0) ba[i] = true; else ba[i] = false; byte[] bya = new byte[(al.Count+7)/8]; ba.CopyTo(bya, 0); Console.WriteLine("Done"); } } } And this is piece of code inside MySql.Data.MySqlClient.PreparedStatement: public CommandResult Execute( MySqlParameterCollection parameters ) { if (parameters.Count < paramList.Length) throw new MySqlException( "Invalid number of parameters for statement execute" ); PacketWriter packet = new PacketWriter(); packet.Driver = (NativeDriver)driver; //TODO: support long data here // create our null bitmap BitArray nullMap = new BitArray( parameters.Count ); //metaData.Length ); for (int x=0; x < parameters.Count; x++) { if (parameters[x].Value == DBNull.Value) nullMap[x] = true; } byte[] nullMapBytes = new byte[ (parameters.Count + 7)/8 ]; nullMap.CopyTo( nullMapBytes, 0 ); As you can see it is almost the same. Suggested fix: Well, how to fix it. I suggest following update: public CommandResult Execute( MySqlParameterCollection parameters ) { if (parameters.Count < paramList.Length) throw new MySqlException( "Invalid number of parameters for statement execute" ); PacketWriter packet = new PacketWriter(); packet.Driver = (NativeDriver)driver; //TODO: support long data here // create our null bitmap BitArray nullMap = new BitArray( parameters.Count ); //metaData.Length ); for (int x=0; x < parameters.Count; x++) { if (parameters[x].Value == DBNull.Value) nullMap[x] = true; } byte[] nullMapBytes = new byte[ (parameters.Count + 7)/8 ]; if(nullMapBytes.Length > 0) // This fix problem with ArgumentException and nullMapBytes is already created and in case of length=0 there is no data to copy... nullMap.CopyTo( nullMapBytes, 0 ); Very simple and now it follows .NET specifications and works with Mono.