Bug #25644 NullPointerException in com.mysql.jdbc.CallableStatement
Submitted: 16 Jan 2007 9:58 Modified: 25 Jan 2007 9:50
Reporter: Lars Borg Email Updates:
Status: Verified Impact on me:
None 
Category:Connector / J Severity:S4 (Feature request)
Version:5.0.4 OS:Microsoft Windows (Windows XP)
Assigned to: Alexander Soklakov CPU Architecture:Any
Triage: D5 (Feature request)

[16 Jan 2007 9:58] Lars Borg
Description:
The server is MySQL 5.0.
Using Hibernate 3.2.

Created a Stored Procedure called getAllVersionsProc. It looks like this:
BEGIN
SELECT ID, someValue, ver
FROM testVersion;
END

(At least, that is what it looks like in my editor...)

When the connector is preparing the call (before the method CallableStatement.execute()) it increments the variable CallableStatement.paramInfo.numParameters to 1 (CallableStatement.java:181).

Later, when CallableStatement.execute() is called, this will result in a NullPointerException at CallableStatement.java:324 (called from line 1938).

Here are the variables in CallableStatement.paramInfo
paramInfo= CallableStatement$CallableStatementParamInfoJDBC3  (id=83)
	- catalogInUse= "asj"
	- isFunctionCall= true
	- nativeSql= "SELECT getAllVersionsProc() "
	- numParameters= 1
	- parameterList= null  <<--------   This is the problematic NULL
	- parameterMap= null
	- this$0 (CallableStatement$CallableStatementParamInfoJDBC3)= CallableStatement  (id=25)
	- this$0 (CallableStatement$CallableStatementParamInfo)= CallableStatement  (id=25)

----------------------------------------------------------
Here is the stacktrace when numParameters are incremented:
----------------------------------------------------------
CallableStatement$CallableStatementParamInfoJDBC3(CallableStatement$CallableStatementParamInfo).<init>(CallableStatement, ResultSet) line: 181
CallableStatement$CallableStatementParamInfoJDBC3.<init>(CallableStatement, ResultSet) line: 343
CallableStatement.convertGetProcedureColumnsToInternalDescriptors(ResultSet) line: 729
CallableStatement.determineParameterTypes() line: 707
CallableStatement.<init>(Connection, String, String, boolean) line: 513
Connection.parseCallableStatement(String) line: 4422
Connection.prepareCall(String, int, int) line: 4496
Connection.prepareCall(String) line: 4470
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object[]) line: 324
GooGooStatementCache$2.run() line: 325
ThreadPoolAsynchronousRunner$PoolThread.run() line: 148

----------------------------------------------------------
And here is the stacktrace when the NullPointerException occurs
----------------------------------------------------------
CallableStatement$CallableStatementParamInfoJDBC3(CallableStatement$CallableStatementParamInfo).iterator() line: 324
CallableStatement.setInOutParamsOnServer() line: 1938
CallableStatement.execute() line: 749
C3P0PooledConnection$1(FilterCallableStatement).execute() line: 402
MySQLDialect.getResultSet(CallableStatement) line: 316
BatchingBatcher(AbstractBatcher).getResultSet(CallableStatement, Dialect) line: 193
CustomLoader(Loader).getResultSet(PreparedStatement, boolean, boolean, RowSelection, SessionImplementor) line: 1665
CustomLoader(Loader).doQuery(SessionImplementor, QueryParameters, boolean) line: 662
CustomLoader(Loader).doQueryAndInitializeNonLazyCollections(SessionImplementor, QueryParameters, boolean) line: 224
CustomLoader(Loader).doList(SessionImplementor, QueryParameters) line: 2144
CustomLoader(Loader).listIgnoreQueryCache(SessionImplementor, QueryParameters) line: 2028
CustomLoader(Loader).list(SessionImplementor, QueryParameters, Set, Type[]) line: 2023
CustomLoader.list(SessionImplementor, QueryParameters) line: 289
SessionImpl.listCustomQuery(CustomQuery, QueryParameters) line: 1695
SessionImpl(AbstractSessionImpl).list(NativeSQLQuerySpecification, QueryParameters) line: 142
SQLQueryImpl.list() line: 150
VersionDAOImpl(HibernateDataAccessObject).getAll(String, SHBTransaction) line: 387
VersionDAOImpl(HibernateDataAccessObject).getAll(String) line: 420
VersionDAOImpl.getAllVersions() line: 19
VersionTest.testVersion1() line: 33
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object[]) line: 324
VersionTest(TestCase).runTest() line: 154
VersionTest(TestCase).runBare() line: 127
TestResult$1.protect() line: 106
TestResult.runProtected(Test, Protectable) line: 124
TestResult.run(TestCase) line: 109
VersionTest(TestCase).run(TestResult) line: 118
TestSuite.runTest(Test, TestResult) line: 208
TestSuite.run(TestResult) line: 203
RemoteTestRunner.runTests(String[], String) line: 436
RemoteTestRunner.run() line: 311
RemoteTestRunner.main(String[]) line: 192

How to repeat:
Just call a zero-parameter SP should do it.

Im using Hibernate to call the SP.

Suggested fix:
Maybe change the declaration in CallableStatementParamInfo (CallableStatement.java:119)

		List parameterList;
to
		List parameterList = new XXXXList();
[16 Jan 2007 11:51] Lars Borg
Further testing revealed that this bug occurs only when Hibernate is used in a certain way...

I'm calling the SP like this, since that is the way they say it should be done:
    <sql-query name="getAllVersionsFunc" callable="true">
        <return alias="v" class="xxxx.data.dto.Version">
            <return-property name="id" column="ID"/>
            <return-property name="someValue" column="someValue"/>
            <return-property name="version" column="ver"/>
        </return>

        { ? = CALL getAllVersionsProc() }

    </sql-query>

I saw that this statement was translated by the connector into:
SELECT getAllVersionsProc()
... and that's a bit strange... it believes it's a function...

So, I changed the original statement in the <sql-query> to just:
CALL getAllVersionsProc()

The result was very satisfying, since all worked just fine.
Also "SELECT getAllVersionsProc()" got a correct answer... "FUNCTION does not exist"

This means that the bug ONLY occurs when the call comes from Hibernate like this:
{ ? = CALL getAllVersionsProc() }

Maybe this is a no-bug candidate, but CallableStatement should definitly behave in a better way in this strange situation.

My suggested fix gives the correct answer "FUNCTION does not exist"
But does this mean that there is a bug in the above mentioned translator? Well, I leave that up to you...
[18 Jan 2007 8:33] Tonci Grgin
Hi Lars and thanks for your report.
>But does this mean that there is a bug in the above mentioned translator? Well,
I leave that up to you...

It should be fairly easy to check that. Just write pure test case (no Hibernate) and see if error occurs. I'm waiting on your results.
[18 Jan 2007 15:03] Mark Matthews
> This means that the bug ONLY occurs when the call comes from Hibernate like > this:
> { ? = CALL getAllVersionsProc() }

You're telling the JDBC driver that this is a function when you use that syntax. I agree we shouldn't throw an NPE in this case (so we'll fix that), but you're using the API incorrectly.

(it's not really clear prior to JDBC-4.0 from the documentation that Sun ships that this is the case, but the escape syntax is based upon ODBC, and it's clear there that you're calling a function. Also note that DatabaseMetaData has methods that will return information for stored functions, and that syntax is how you call them.)
[22 Jan 2007 9:30] Lars Borg
I just tested calling "{ ? = CALL getAllversionsProc() }" without Hibernate and that generates no NPE, it just says "FUNCTION asj.getAllVersionsProc does not exist".

But when I'm calling the same statement using Hibernate, I get the NPE.
[25 Jan 2007 9:50] Tonci Grgin
I am setting this report to "Verified" on the basis of Mark's remark:

"I agree we shouldn't throw an NPE in this case (so we'll fix that), but you're
using the API incorrectly."