Skip to content

Commit

Permalink
fix: allow OUT parameter registration when using CallableStatement na…
Browse files Browse the repository at this point in the history
…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 ed74670
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 0 deletions.
15 changes: 15 additions & 0 deletions pgjdbc/src/main/java/org/postgresql/core/Parser.java
Expand Up @@ -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) {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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();
}
Expand Down Expand Up @@ -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.