Skip to content
Permalink
Browse files

fix: allow OUT parameter registration when using CallableStatement na…

…tive CALL (#1561)

Currently, using CallableStatement, the PGJDBC driver will allow invocation of procedures using
the native "call myproc(...)" syntax, provided the procedure only has IN parameters or no
parameters.
However, if the procedure has any INOUT parameters, then an attempt to invoke the
CallableStatement's registerOutParameter() method, which is required in order to register the OUT
part of the INOUT parameter, will fail with the following error:
   This statement does not declare an OUT parameter.  Use { ?= call ... } to declare one.

This fixes issue: #1545
  • Loading branch information
gregn123 authored and davecramer committed Nov 26, 2019
1 parent d755913 commit ed74670fae932935a156eccfb4b1ff16758f5693
@@ -1042,6 +1042,21 @@ public static JdbcCallParseInfo modifyJdbcCall(String jdbcSql, boolean stdString
if (i == len && !syntaxError) {
if (state == 1) {
// Not an escaped syntax.

// Detect PostgreSQL native CALL.
// (OUT parameter registration, needed for stored procedures with INOUT arguments, will fail without this)
i = 0;
while (i < len && Character.isWhitespace(jdbcSql.charAt(i))) {
i++; // skip any preceding whitespace
}
if (i < len - 5) { // 5 == length of "call" + 1 whitespace
//Check for CALL followed by whitespace
char ch = jdbcSql.charAt(i);
if ((ch == 'c' || ch == 'C') && jdbcSql.substring(i, i + 4).equalsIgnoreCase("call")
&& Character.isWhitespace(jdbcSql.charAt(i + 4))) {
isFunction = true;
}
}
return new JdbcCallParseInfo(sql, isFunction);
}
if (state != 8) {
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.postgresql.core.ServerVersion;
import org.postgresql.test.TestUtil;
import org.postgresql.test.jdbc2.BaseTest4;
import org.postgresql.util.PSQLState;
@@ -84,6 +85,12 @@ public void setUp() throws Exception {
+ "end;'"
+ "LANGUAGE plpgsql VOLATILE;"
);
if (TestUtil.haveMinimumServerVersion(con, ServerVersion.v11)) {
stmt.execute(
"CREATE OR REPLACE PROCEDURE inonlyprocedure(a IN int) AS 'BEGIN NULL; END;' LANGUAGE plpgsql");
stmt.execute(
"CREATE OR REPLACE PROCEDURE inoutprocedure(a INOUT int) AS 'BEGIN a := a + a; END;' LANGUAGE plpgsql");
}
}

@Override
@@ -97,6 +104,10 @@ public void tearDown() throws SQLException {
stmt.execute("drop function myif(a INOUT int, b IN int)");
stmt.execute("drop function mynoparams()");
stmt.execute("drop function mynoparamsproc()");
if (TestUtil.haveMinimumServerVersion(con, ServerVersion.v11)) {
stmt.execute("drop procedure inonlyprocedure(a IN int)");
stmt.execute("drop procedure inoutprocedure(a INOUT int)");
}
stmt.close();
super.tearDown();
}
@@ -1034,4 +1045,26 @@ public void testProcedureNoParametersWithoutParentheses() throws SQLException {
TestUtil.closeQuietly(cs);
}

@Test
public void testProcedureInOnlyNativeCall() throws SQLException {
assumeMinimumServerVersion(ServerVersion.v11);
assumeCallableStatementsSupported();
CallableStatement cs = con.prepareCall("call inonlyprocedure(?)");
cs.setInt(1, 5);
cs.execute();
TestUtil.closeQuietly(cs);
}

@Test
public void testProcedureInOutNativeCall() throws SQLException {
assumeMinimumServerVersion(ServerVersion.v11);
assumeCallableStatementsSupported();
// inoutprocedure(a INOUT int) returns a*2 via the INOUT parameter
CallableStatement cs = con.prepareCall("call inoutprocedure(?)");
cs.setInt(1, 5);
cs.registerOutParameter(1, Types.INTEGER);
cs.execute();
assertEquals("call inoutprocedure(?) should return 10 (when input param = 5) via the INOUT parameter, but did not.", 10, cs.getInt(1));
TestUtil.closeQuietly(cs);
}
}

0 comments on commit ed74670

Please sign in to comment.
You can’t perform that action at this time.