Bug #1658 provide exception name when java.lang.ArrayIndexOutOfBoundsException
Submitted: 24 Oct 2003 23:46 Modified: 3 Dec 2003 9:05
Reporter: Ralf Hauser Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S4 (Feature request)
Version:3.1 nightly oct 3 OS:Linux (RH9)
Assigned to: Mark Matthews CPU Architecture:Any

[24 Oct 2003 23:46] Ralf Hauser
Description:
I forgot to add some ? to a prepared statement.
An sql exception was thrown, but it only gave a number "26".

Please make the error a little bit more verbose! Once (after a few re-compiles) I added a printStackTrace(); I finally could understand what the problem was:

java.lang.ArrayIndexOutOfBoundsException: 26
        at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:1
955)
        at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:1
977)
        at com.mysql.jdbc.PreparedStatement.setInt(PreparedStatement.java:620)
...

How to repeat:
.

Suggested fix:
more verbose error information - at least mention the java.lang.ArrayIndexOutOfBoundsException
[25 Oct 2003 1:04] Ralf Hauser
title was incomplete
[25 Oct 2003 6:42] Mark Matthews
Hmm. You should actually get the exception "Parameter index out of range" in this case, because the bounds are checked in setInternal(), and if out of range, a SQLException is thrown, not an ArrayOutOfBoundsException.

Do you have a testcase that you can give us to see why this test is failing?
[25 Oct 2003 7:37] Ralf Hauser
/** Statment update user */
			updUserStmt =
				connWrite.prepareStatement(
					"UPDATE "
						+ TBL_USER
						+ " SET "
						+ "login = ? , "
						+ "forename = ? ,"
...
						+ "signature = ? , "
						+ "sig_incl  "
						+ " WHERE user_id = ? ;");

==> one "?" missing, but

		try {
			updUserStmt.setString(1, user.getLogin()); //login			
			updUserStmt.setString(2, user.getForename()); //forename
...
			updUserStmt.setString(25, user.getSignature());
			updUserStmt.setInt(26,
                            (user.getSigIncl().booleanValue()) ? 1 : 0);
			updUserStmt.setInt(27, user.getUserId().intValue());
			log.debug("updUserStmt: " + updUserStmt.toString());
			int retVal = updUserStmt.executeUpdate();
...

need more?
[25 Oct 2003 8:30] Mark Matthews
Two questions...Are you _sure_ you're using 3.1 nightly from Oct 3? If I pull it down and try and match up the stack trace to the source code, the line numbers don't match (not even close, 100's of lines off). 

Could you try a newer nightly snapshot? The 'new' snapshots page is http://downloads.mysql.com/snapshots.php

The reason I ask for answers to both of these questions is that I can't reproduce the problem with the test case you gave me, and the stack trace looks a bit 'odd' (not matching up with the version you say you are using).
[25 Oct 2003 10:07] Ralf Hauser
apologies, since I had the 3.0.8 stable in the cvs, the nightly got overwritten with that..
Got myself now the 031025 and the line numbers are changed:

java.lang.ArrayIndexOutOfBoundsException: 26
        at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:2
127)
        at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:2
144)
        at com.mysql.jdbc.PreparedStatement.setInt(PreparedStatement.java:660)
        at com.privasphere.privalope.db.MySqlSystem.updateUser(MySqlSystem.java:
5532)
[25 Oct 2003 19:16] Mark Matthews
Is there more to the testcase that you're not showing? What you've provided throws a SQLException (not an ArrayIndexOutOfBoundsException) when I try it.
[25 Oct 2003 23:27] Ralf Hauser
yes, since the 
		} catch (SQLException e) {
			e.printStackTrace();
			log.error(e.getMessage());
			throw new DatabaseException(e.getMessage());
		}
I placed after the updUserStmt didn't really get me more information, 
I put far away from the real problem code a 

						} catch (Exception e) {
							e.printStackTrace();
						log.error(e.getMessage());

that finally surfaced the real problem...
[26 Oct 2003 5:29] Mark Matthews
I'm sorry, but you must've misunderstood me. The query and prepared statement code you gave for your testcase, causes an SQLException (like it should), not an ArrayIndexOutOfBoundsException like you are getting. 

Your testcase contains a line '...', which I assume 'more similar stuff'. However, I believe it is either the value for TBL_USER or whatever is in '...' that is causing the error, neither values you have provided.
[26 Oct 2003 20:50] Ralf Hauser
> Thanks for the extra code. I still can't reproduce this error in 3.1 or
> 3.0 (latest versions of each). I'm not saying it doesn't exist, but it
> seems to be something particular to your setup.
> 
> Can you step through a debugger and give me a 'lay of the land' when
> this is running on your end? A good breakpoint would be set would be on
> the line in your code when you're setting parameters 26 and 27 (I'm
> guessing). If you then run through the check for parameter out-of-bounds
> in setInternal(), you should see why it's not catching it.
I never said that it is not catching it. I just doesn't provide any error information beyond the index number.
Since I am running this on a tomcat server, how do you recommend to get the 'lay of the land' - my remote 10 years back gdb experience suggests that I run the code that is to be debugged in a GUI which I obviously don't have in this case.
[27 Oct 2003 5:59] Mark Matthews
The exception I get from your test case is along the lines of:

java.sql.SQLException: Parameter index out of range (100 > 27).
	at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:2118)
	at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:2144)
	at com.mysql.jdbc.PreparedStatement.setInt(PreparedStatement.java:660)
	at testsuite.regression.StatementRegressionTest.testParameterBoundsCheck(StatementRegressionTest.java:420)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at junit.framework.TestCase.runTest(TestCase.java:154)
	at junit.framework.TestCase.runBare(TestCase.java:127)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:118)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:395)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:279)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:171)

Notice that there's no ArrayIndexOutOfBoundsException there, and the message is clearer.

To remotely debug an app on Tomxat, I recommend you start tomcat with remote debugging and use an IDE with a graphical debugger (like Eclipse (www.eclipse.org) for best results), and import your code into it. Some tips:

Set the environment variable 'JPDA_TRANSPORT' to 'dt_socket'
Set the environment variable 'JPDA_ADDRESS' to '8000'

Start tomcat using the catalina.sh or catalina.bat script (whichever is appropriate to your platform), passing the command-line arguments 'jpda run':

./catalina.sh jpda run

Once tomcat is up and running, start a remote debugging session to the instance. In Eclipse, you would first select the 'project' that contains your code (setting any breakpoints you want before hand by double-clicking in the left-hand margin), click on the Debug menu-bar button, choose 'Debug' at the bottom of the menu, choose the 'Remote Java Application' item in the tree, hit the 'New' button, and then hit the 'Debug' button at the lower-right corner of the dialog box. You then hit whatever sequence of web pages that causes the error, and the debugger should stop at your breakpoint. You can then use the debugger tools to step through the code in question.

You can also start tomcat in 'debug' mode (i.e. ./catalina.sh debug) which will drop you straight into 'jdb' which is a lot like GDB. You can then set breakpoints, and examine code by following the directions at http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/jdb.html
[23 Nov 2003 15:33] Ralf Hauser
Mark, sorry for taking so long to follow up on this.
I am having trouble - perhaps because I am doing that remotely with a 64/256 ADSL?

first I created a 
/usr/local/tomcat/bin/debug.sh with the following content:
<<#!/bin/sh
export JPDA_TRANSPORT=dt_socket
export JPDA_ADDRESS=8000
/usr/local/tomcat/bin/catalina.sh jpda run &
tail -f /usr/local/tomcat/logs/catalina.out>>

then I connected with the following tunnel:
rhauser@PCK:~/<5>workspace/myproj> ssh -C hauser@my.tomcatServer.domain -L 8000:localhost:8000 -f "sleep 300"

Result:
- I see multiple threads, but browsing on the application now gets terribly slow. Or would you say that this can only be done with a local tomcat?
- the error still occurs, but somehow, it doesn't appear to stop at the blue
  breakpoint I set such that I could try to get the "lay of the land" you wish to see. How would I best get that?
- furthermore, no catalina.out gets created anymore with my debug.sh.
[2 Dec 2003 9:30] Mark Matthews
Local debugging is going to work better for you (if you can at all arrange it). What you want to do is set an exception breakpoint on java.lang.ArrayIndexOutOfBounds exception, and the debugger should stop where the exception is thrown. You can then look at the call stack, and work your way back up the methods that have called the code that throws the exception to see what is going on.

I am still _very_ confused as to how you get an ArrayIndexOutOfBounds exception, since there's an if() statement that checks the bounds before the array is accessed :(
[3 Dec 2003 2:41] Ralf Hauser
quick interim report:
java.lang.ArrayIndexOutOfBoundsException: 27
        at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:2155)
        at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:1134)
        at org.apache.commons.dbcp.DelegatingPreparedStatement.setString(DelegatingPreparedStatement.java:243)

moved to commons.dbcp pool, but still can reproduce the error with mysql-connector-java-3.1-nightly-20031203-bin.jar
[3 Dec 2003 9:05] Mark Matthews
Okay I found the issue (nad fixed it). The problem was an off-by-one error in the bounds checking for the driver, that would only manifest itself when the parameter index you used was _exactly_ 1 higher than the number of parameters that were actually in the statement.

You should be able to check out a nightly build after 00:00 GMT to get the fix.

Thanks for your patience on this issue!