Bug #23749 VarChar field size over 255 causes a System.OverflowException
Submitted: 29 Oct 2006 12:28 Modified: 6 Nov 2006 17:46
Reporter: simon bosanquet Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S1 (Critical)
Version:1.0.8 RC OS:Microsoft Windows (WinXP SP2)
Assigned to: Reggie Burnett CPU Architecture:Any

[29 Oct 2006 12:28] simon bosanquet
Description:
If you have a stored procedure which has a varchar field size over 255 then an exception [System.OverflowException] is generated.  This is because the code [around line 367 of StoredProcedure.cs] is parsing for a byte and the value being passed in is greater than what can be stored in a byte (p.Size = p.Precision = Byte.Parse(parts[0]);).  parts[0] is the size of the varchar.  In my application i have some varchar fields over 255.  Some go as big as 2500.

Also, in StoredProcedure.cs you are limiting p.Size to a maximum value of p.precision due to the way in which the values are being written.

Exception:

Value was either too large or too small for an unsigned byte. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.OverflowException: Value was either too large or too small for an unsigned byte.

Source Error: 

Line 368://				try
Line 369://				{	
Line 370:					p.Size = p.Precision = Byte.Parse(parts[0]);
Line 371://				}
Line 372://				catch 
 

Source File: c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs    Line: 370 

Stack Trace: 

[OverflowException: Value was either too large or too small for an unsigned byte.]
   System.Byte.Parse(String s, NumberStyles style, NumberFormatInfo info) +2577833
   System.Byte.Parse(String s) +20
   MySql.Data.MySqlClient.StoredProcedure.ParseType(String type, String sql_mode, MySqlParameter p) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:370
   MySql.Data.MySqlClient.StoredProcedure.ParseParameter(String parmDef, ContextString cs, String sqlMode) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:228
   MySql.Data.MySqlClient.StoredProcedure.ParseBody(String body, String sqlMode) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:185
   MySql.Data.MySqlClient.StoredProcedure.DiscoverParameters(String spName) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:238
   MySql.Data.MySqlClient.ProcedureCache.GetProcData(MySqlConnection connection, String spName) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\ProcedureCache.cs:108
   MySql.Data.MySqlClient.ProcedureCache.AddNew(MySqlConnection connection, String spName) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\ProcedureCache.cs:81
   MySql.Data.MySqlClient.ProcedureCache.GetProcedure(MySqlConnection conn, String spName) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\ProcedureCache.cs:69
   MySql.Data.MySqlClient.StoredProcedure.Prepare(MySqlCommand cmd) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:261

[MySqlException: Exception during execution of 'DNF_USERS_GET': Value was either too large or too small for an unsigned byte.]
   MySql.Data.MySqlClient.StoredProcedure.Prepare(MySqlCommand cmd) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\StoredProcedure.cs:312
   MySql.Data.MySqlClient.MySqlCommand.PrepareSqlBuffers(String sql) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\command.cs:551
   MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior) in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\command.cs:413
   MySql.Data.MySqlClient.MySqlCommand.ExecuteReader() in c:\Program Files\MySQL\MySQL Connector Net 1.0.8\Source\MySqlClient\command.cs:383
   DotNetForum.Library.DAL.MySqlDB.UserGet(UserSession Session, Int64 UserID) in d:\Misc\SF\dnfBB_SVN\v1\DNFLib\DAL\MySQLDB.cs:4300
   DotNetForum.Library.BOL.UserSession.GetUserDetails() in d:\Misc\SF\dnfBB_SVN\v1\DNFLib\BOL\UserSession.cs:154
   DotNetForum.Library.BOL.UserSession..ctor(String SessionID, Boolean LoggedIn, Boolean UseInternalCache) in d:\Misc\SF\dnfBB_SVN\v1\DNFLib\BOL\UserSession.cs:145
   DotNetForum.Library.BOL.UserSession.LoginGuestUser(Int64 BoardID, Int32 Days, Boolean UseInternalCache, String IPAddress) in d:\Misc\SF\dnfBB_SVN\v1\DNFLib\BOL\UserSession.cs:332
   DotNetForum.WebSite.Classes.BaseWebForm.LoginUser() in d:\Misc\SF\dnfBB_SVN\v1\wwwroot\Classes\BaseWebFormClass.cs:257
   DotNetForum.WebSite.Classes.BaseWebForm.GetSession() in d:\Misc\SF\dnfBB_SVN\v1\wwwroot\Classes\BaseWebFormClass.cs:283
   DotNetForum.WebSite.Classes.BaseWebForm.OnInit(EventArgs e) in d:\Misc\SF\dnfBB_SVN\v1\wwwroot\Classes\BaseWebFormClass.cs:62
   DotNetForum.WebSite.ViewForum.OnInit(EventArgs e) in d:\Misc\SF\dnfBB_SVN\v1\wwwroot\ViewForum.aspx.cs:302
   System.Web.UI.Control.InitRecursive(Control namingContainer) +321
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +692

 

--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.42; ASP.NET Version:2.0.50727.210 

How to repeat:
Create a stored procedure with a field type of VarChar(256).  When you come to read in the VarChar from your program the exception is generated.

Suggested fix:
Split p.Size and p.Precision into seperate allocations.  Use p.Size=int.Parse(parts[0]) rather than Byte.Parse.  Not sure what to do with p.Precision at the moment.
[29 Oct 2006 12:40] simon bosanquet
The documentation states that it can have a value up to 65,532 in versions after MySQL 5.0.3.

"Values in VARCHAR columns are variable-length strings. The length can be specified as a value from 0 to 255 before MySQL 5.0.3, and 0 to 65,535 in 5.0.3 and later versions. (The maximum effective length of a VARCHAR in MySQL 5.0.3 and later is determined by the maximum row size and the character set used. The maximum column length is subject to a row size of 65,532 bytes.)"
[29 Oct 2006 13:15] simon bosanquet
Example Project

Attachment: LARGE_VARCHAR_EXAMPLE.zip (application/x-zip-compressed, text), 21.77 KiB.

[29 Oct 2006 13:16] simon bosanquet
SQL Example File

Attachment: LargeVarcharExample.sql (application/octet-stream, text), 1.35 KiB.

[29 Oct 2006 13:49] simon bosanquet
I have just been looking at the 5.0.1 source and i see you have modified the ParseType function [now contained in ISSchemaProvider.cs].  This seems to be a better implementation.

Resolution:

________________________________________________________________________________
Old Code:

private void ParseType(string type, string sql_mode, MySqlParameter p)
		{
			string typeName, flags = String.Empty, size;
			int end;

			type = type.ToLower(CultureInfo.InvariantCulture).Trim();
			int start = type.IndexOf("(");
			if (start != -1)
				end = type.IndexOf(')', start + 1);
			else
				end = start = type.IndexOf(' ');
			if (start == -1)
				start = type.Length;

			typeName = type.Substring(0, start);
			if (end != -1)
				flags = type.Substring(end + 1);
			bool unsigned = flags.IndexOf("unsigned") != -1;
			bool real_as_float = sql_mode.IndexOf("REAL_AS_FLOAT") != -1;

			p.MySqlDbType = GetTypeFromName(typeName, unsigned, real_as_float);

			if (end > start && p.MySqlDbType != MySqlDbType.Set)
			{
				size = type.Substring(start + 1, end - (start + 1));
				string[] parts = size.Split(new char[] { ',' });
				p.Size = p.Precision = Byte.Parse(parts[0]);
				if (parts.Length > 1)
					p.Scale = Byte.Parse(parts[1]);
			}
		}

________________________________________________________________________________
New Code: 

private void ParseType(string type, string sql_mode, MySqlParameter p)
		{
			string typeName, flags = String.Empty, size;
			int end;

			type = type.ToLower(CultureInfo.InvariantCulture).Trim();
			int start = type.IndexOf("(");
			if (start != -1)
				end = type.IndexOf(')', start + 1);
			else
				end = start = type.IndexOf(' ');
			if (start == -1)
				start = type.Length;

			typeName = type.Substring(0, start);
			if (end != -1)
				flags = type.Substring(end + 1);
			bool unsigned = flags.IndexOf("unsigned") != -1;
			bool real_as_float = sql_mode.IndexOf("REAL_AS_FLOAT") != -1;

			p.MySqlDbType = GetTypeFromName(typeName, unsigned, real_as_float);

			if (end > start && p.MySqlDbType != MySqlDbType.Set)
			{
				size = type.Substring(start + 1, end - (start + 1));
				string[] parts = size.Split(new char[] { ',' });	

                if (typeName == "varchar" || typeName == "char")
                    p.Size = Int32.Parse(parts[0]);
                else
                {
                    p.Precision = Byte.Parse(parts[0]);
                    if (parts.Length > 1)
                        p.Scale = Int32.Parse(parts[1]);
                }
			}
		}
[30 Oct 2006 15:36] Tonci Grgin
Hi Simon and thanks for your complete bug report.
Verified as described by reporter on:
  - MySQL server 5.0.27BK on WinXP Pro SP2 localhost
  - c/NET 1.0.8RC SVN
  - Net FW 2.0

StoredProcedure.cs, line 368
if (end > start && p.MySqlDbType != MySqlDbType.Set)
{
	size = type.Substring(start + 1, end - (start + 1));
	string[] parts = size.Split(new char[] { ',' });
	p.Size = p.Precision = Byte.Parse(parts[0]);
	if (parts.Length > 1)
		p.Scale = Byte.Parse(parts[1]);
}
[30 Oct 2006 16:48] 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/14574
[30 Oct 2006 16:50] 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/14575
[6 Nov 2006 14:11] MC Brown
A note has been added to the 1.0.9 changelog.
[6 Nov 2006 17:46] simon bosanquet
You say 1.0.9 change log.  Does this mean it wont be in 1.0.8 final?
[6 Nov 2006 17:56] Reggie Burnett
1.0.8 is an RC.  1.0.9 Final will be the very next release.