Permalink
Browse files

feat: support Types.REF_CURSOR

JDBC 4.2 officially supports ref cursors through Types.REF_CURSOR.
Previously users had to use Types.OTHER for registering a ref cursor
out parameter. It is important to preserve this behavior in order to
keep existing code working.

This commit includes the following changes:

 - register the type REF_CURSOR in TypeInfoCache
 - register the REF_CURSOR Oid
 - slightly relax the function return type check to continue allowing
   registering ref cursors with OTHER
 - add tests that verify ref cursors can be registered with both
   REF_CURSOR and OTHER
 - update PgDatabaseMetaData to report that we support ref cursors
 - update SimpleJdbc42Test to verify we correctly report ref cursor
   support

Fixes #633
closes #635
  • Loading branch information...
marschall authored and vlsi committed Sep 6, 2016
1 parent 2d70385 commit b5c3f592c2c07fecf9f91b6a2b1a93d0362e3e8b
@@ -3,7 +3,7 @@
<parent>
<groupId>org.postgresql</groupId>
<artifactId>pgjdbc-core-parent</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
<relativePath />
</parent>

@@ -76,6 +76,8 @@
public static final int JSONB_ARRAY = 3807;
public static final int JSON = 114;
public static final int JSON_ARRAY = 199;
public static final int REF_CURSOR = 1790;
public static final int REF_CURSOR_ARRAY = 2201;

/**
* Returns the name of the oid as string.
@@ -133,6 +133,12 @@ public boolean executeWithFlags(int flags) throws SQLException {
if (callResult[j] != null) {
callResult[j] = ((Double) callResult[j]).floatValue();
}
//#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
} else if (columnType == Types.REF_CURSOR && functionReturnType[j] == Types.OTHER) {
// For backwards compatibility reasons we support that ref cursors can be
// registered with both Types.OTHER and Types.REF_CURSOR so we allow
// this specific mismatch
//#endif
} else {
throw new PSQLException(GT.tr(
"A CallableStatement function was executed and the out parameter {0} was of type {1} however type {2} was registered.",
@@ -3153,7 +3153,7 @@ public long getMaxLogicalLobSize() throws SQLException {
}

public boolean supportsRefCursors() throws SQLException {
return false;
return true;
}

public RowIdLifetime getRowIdLifetime() throws SQLException {
@@ -67,8 +67,6 @@
// 2 - sql type
// 3 - java class
// 4 - array type oid
// 5 - conditional minimum server version
// 6 - conditional minimum JDK build version
private static final Object types[][] = {
{"int2", Oid.INT2, Types.SMALLINT, "java.lang.Integer", Oid.INT2_ARRAY},
{"int4", Oid.INT4, Types.INTEGER, "java.lang.Integer", Oid.INT4_ARRAY},
@@ -92,6 +90,9 @@
{"timestamp", Oid.TIMESTAMP, Types.TIMESTAMP, "java.sql.Timestamp", Oid.TIMESTAMP_ARRAY},
{"timestamptz", Oid.TIMESTAMPTZ, Types.TIMESTAMP, "java.sql.Timestamp",
Oid.TIMESTAMPTZ_ARRAY},
//#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
{"refcursor", Oid.REF_CURSOR, Types.REF_CURSOR, "java.sql.ResultSet", Oid.REF_CURSOR_ARRAY},
//#endif
{"json", Oid.JSON, Types.VARCHAR, "java.lang.String", Oid.JSON_ARRAY},
{"point", Oid.POINT, Types.OTHER, "org.postgresql.geometric.PGpoint", Oid.POINT_ARRAY}
};
@@ -15,21 +15,44 @@
import org.postgresql.test.TestUtil;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Arrays;

/*
/**
* RefCursor ResultSet tests. This test case is basically the same as the ResultSet test case.
*
* @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
* <p>For backwards compatibility reasons we verify that ref cursors can be
* registered with both {@link Types#OTHER} and {@link Types#REF_CURSOR}.</p>
*
* @author Nic Ferrier (nferrier@tapsellferrier.co.uk)
*/
@RunWith(Parameterized.class)
public class RefCursorTest extends BaseTest4 {

final int cursorType;

public RefCursorTest(String typeName, int cursorType) {
this.cursorType = cursorType;
}

@Parameterized.Parameters(name = "typeName = {0}, cursorType = {1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][]{
{"OTHER", Types.OTHER},
//#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
{"REF_CURSOR", Types.REF_CURSOR},
//#endif
});
}

@Override
public void setUp() throws Exception {
// this is the same as the ResultSet setup.
@@ -71,7 +94,7 @@ public void tearDown() throws SQLException {
public void testResult() throws SQLException {
assumeCallableStatementsSupported();
CallableStatement call = con.prepareCall("{ ? = call testspg__getRefcursor () }");
call.registerOutParameter(1, Types.OTHER);
call.registerOutParameter(1, cursorType);
call.execute();
ResultSet rs = (ResultSet) call.getObject(1);

@@ -94,6 +117,7 @@ public void testResult() throws SQLException {
assertTrue(rs.getInt(1) == 9);

assertTrue(!rs.next());
rs.close();

call.close();
}
@@ -103,11 +127,12 @@ public void testResult() throws SQLException {
public void testEmptyResult() throws SQLException {
assumeCallableStatementsSupported();
CallableStatement call = con.prepareCall("{ ? = call testspg__getEmptyRefcursor () }");
call.registerOutParameter(1, Types.OTHER);
call.registerOutParameter(1, cursorType);
call.execute();

ResultSet rs = (ResultSet) call.getObject(1);
assertTrue(!rs.next());
rs.close();

call.close();
}
@@ -117,7 +142,7 @@ public void testMetaData() throws SQLException {
assumeCallableStatementsSupported();

CallableStatement call = con.prepareCall("{ ? = call testspg__getRefcursor () }");
call.registerOutParameter(1, Types.OTHER);
call.registerOutParameter(1, cursorType);
call.execute();

ResultSet rs = (ResultSet) call.getObject(1);
@@ -136,7 +161,7 @@ public void testResultType() throws SQLException {
assumeCallableStatementsSupported();
CallableStatement call = con.prepareCall("{ ? = call testspg__getRefcursor () }",
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
call.registerOutParameter(1, Types.OTHER);
call.registerOutParameter(1, cursorType);
call.execute();
ResultSet rs = (ResultSet) call.getObject(1);

@@ -145,6 +170,8 @@ public void testResultType() throws SQLException {

assertTrue(rs.last());
assertEquals(6, rs.getRow());
rs.close();
call.close();
}

}
@@ -8,6 +8,8 @@

package org.postgresql.test.jdbc42;

import static org.junit.Assert.assertTrue;

import org.postgresql.test.TestUtil;

import org.junit.After;
@@ -37,7 +39,7 @@ public void tearDown() throws Exception {
* Test presence of JDBC 4.2 specific methods
*/
@Test
public void testDatabaseMetaData() throws Exception {
_conn.getMetaData().supportsRefCursors();
public void testSupportsRefCursors() throws Exception {
assertTrue(_conn.getMetaData().supportsRefCursors());
}
}
@@ -3,7 +3,7 @@
<parent>
<groupId>org.postgresql</groupId>
<artifactId>pgjdbc-versions</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
</parent>

<artifactId>pgjdbc-aggregate</artifactId>
@@ -31,7 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
<parent>
<groupId>org.postgresql</groupId>
<artifactId>pgjdbc-versions</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
<relativePath />
</parent>

@@ -0,0 +1,4 @@
No test source yet, however the folder is required for java comment preprocessor
Otherwise it fails with

[ERROR] Failed to execute goal com.igormaznitsa:jcp:6.0.1:preprocess (preprocessTestSources) on project pgjdbc-benchmark: Can't find a source directory [/home/travis/build/pgjdbc/pgjdbc/ubenchmark/src/test/java] -> [Help 1]

0 comments on commit b5c3f59

Please sign in to comment.