Bug #39510 Certain Stored Procedures Fail with a NumberFormatException
Submitted: 18 Sep 2008 8:23 Modified: 18 Sep 2008 16:46
Reporter: Doug Lawrence Email Updates:
Status: Duplicate Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.1.6 OS:Any
Assigned to: CPU Architecture:Any
Tags: Contribution

[18 Sep 2008 8:23] Doug Lawrence
Description:
Stored functions that contain certain keywords will fail because of the way the connector is iterating through keywords. The most obvious test case involves any stored function that contains a comma, followed by the word NOT.

The findEndOfReturnsClause function in the DatabaseMetaData class defines these tokens in this order:

String[] tokens = new String[] { "LANGUAGE", "NOT", "DETERMINISTIC",
				"CONTAINS", "NO", "READ", "MODIFIES", "SQL", "COMMENT", "BEGIN", 
				"RETURN" };

How to repeat:
Create table and function:

create table x (id int not null, val varchar(100) null);

delimiter ;;
CREATE FUNCTION getNotNull() RETURNS int(11)
BEGIN
	declare retval int(11);
	
	select count(*) into retval
	from x 
	where id in (1, 2)
	and   val IS NOT NULL;
	
	return retval;
END;
;;
delimiter ;

insert into x values (1, null), (1, 'test'), (2, 'test'), (3, 'test'), (4, null);

Create java program:

package test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;

public class TestBug {
	
	public static void main(String args[]) throws Throwable {
		String dbUrl = "<yourdb>";
		String dbUser = "<youruser>";
		String dbPass = "<yourpass>";
		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPass);
		CallableStatement cs = conn.prepareCall("{? = call getNotNull()}");
		cs.registerOutParameter(1, Types.INTEGER);
		cs.execute();
		
		System.out.println(cs.getInt(1));
	}

}

Execute, and get the following error:

Exception in thread "main" java.sql.SQLException: java.lang.NumberFormatException: For input string: "11)
BEGIN
declare retval int(11);
select count(*) into retval
from x 
where id in (1"
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:430)
	at com.mysql.jdbc.CallableStatement.getInstance(CallableStatement.java:505)
	at com.mysql.jdbc.ConnectionImpl.parseCallableStatement(ConnectionImpl.java:3881)
	at com.mysql.jdbc.ConnectionImpl.prepareCall(ConnectionImpl.java:3965)
	at com.mysql.jdbc.ConnectionImpl.prepareCall(ConnectionImpl.java:3939)
	at test.TestBug.main(TestBug.java:16)

Suggested fix:
The problem is that the first pass in the DatabaseMetaData takes a lot more data than just the return value. It's often hidden by later processing that looks for the first (, but before that happens, there is a check for a comma that is causing it to choke. It seems like the cleanest solution is to return the correct data in that first pass. Rather than guess what that should be, this solution takes the closest index of a keyword to the RETURNS keyword. 

Replace the code at 1893:

int startLookingAt = positionOfReturnKeyword + "RETURNS".length() + 1;

		for (int i = 0; i < tokens.length; i++) {
			int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes(
					startLookingAt, procedureDefn, tokens[i], quoteChar
							.charAt(0), !this.conn.isNoBackslashEscapesSet());

			if (endOfReturn != -1) {
				return endOfReturn;
			}
		}

with this:

int firstKeyword = Integer.MAX_VALUE;
		for (int i = 0; i < tokens.length; i++) {
			int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes(
					startLookingAt, procedureDefn, tokens[i], quoteChar
							.charAt(0), !this.conn.isNoBackslashEscapesSet());

			if (endOfReturn >= 0 && endOfReturn < firstKeyword) {
				firstKeyword = endOfReturn;				
			}
		}
		if(firstKeyword < Integer.MAX_VALUE) {
			return firstKeyword;
		}
[18 Sep 2008 8:26] Doug Lawrence
Not sure what the patch file format should be, but here is the diff.

Attachment: patch.diff (application/octet-stream, text), 319 bytes.

[18 Sep 2008 8:27] Doug Lawrence
Patched file

Attachment: DatabaseMetaData.java (text/plain), 246.13 KiB.

[18 Sep 2008 16:46] Sveta Smirnova
Thank you for the report.

This bug is duplicate of bug #35199. Please use 5.1 snapshot available from http://downloads.mysql.com/snapshots.php
[22 Jul 2015 13:47] Lindley Smith
I'm using 5.1.6 (supposedly) and getting the error log in this bug report, which seems different to the error log in the 'duplicate' error 35199. Was this really a duplicate? Was it fixed? Which version?