3
votes

I am on a team that develops a business intelligence (reporting) tool. We report off many sources include Stored Procedures. We use the meta-data provided by the JDBC driver to determine the input and output paramters of a Stored Procedure.

It appears that PostgreSQL 9 JDBC drivers are incorrectly returning meta-data for the parameters of a procedure.

For instance my stored procedure looks like this:

CREATE FUNCTION person(personid int)
RETURNS TABLE(person int, name varchar(200)) AS $$
BEGIN
    RETURN QUERY SELECT ipperson, firstname FROM person
                 WHERE ipperson = personid;
END;
$$ LANGUAGE plpgsql;

So it has one paramter in, two columns returned in a resultset.

The PostgreSQL driver is reporting that there are 3 IN parameters.

  • personid (the parameter)
  • person (first column returned)
  • name (second column returned)

with no meta-data to distinguish between types.

I execute this with:

SELECT * FROM person(?);

(As a prepared statement, setting values for each ? token)

I know that I can filter using the columns returned like this:

SELECT * FROM person(5) where person = 5;

But I am more interested in getting only the parameters returned by the meta-data, so I can programmatically build the query string (I need to know how many ?'s to put in the query).

Not sure if this is a bug, or whether I am doing something wrong.

If I use a PostgreSQL 8 driver, it seems to return the correct number of parameters:

  • personid (the parameter)

Thanks.

Specific Driver/Server versions are:

  • PostgreSQL Server 9.11 (Mint 16)
  • PostgreSQL "8" 8.0 JDBC3 with SSL(build 313)
  • PostgreSQL "9" 9.3 JDBC4 (build 1100)

Additional Information to replicate what I am seeing:

DB Scripts:

CREATE TABLE testtable (
    id integer,
    name varchar
);

INSERT INTO testtable VALUES (1, 'Bob');

CREATE FUNCTION testproc(itemid int)
RETURNS TABLE(id int, name varchar(200)) AS $$
BEGIN
    RETURN QUERY SELECT ipperson, firstname FROM testtable
                 WHERE id = itemid;
END;
$$ LANGUAGE plpgsql;

Java Code:

package com.hof.unittest;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

public class TestPostgres {

    public static void main(String args[]) {

        try {

            Class.forName("org.postgresql.Driver");

            Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/testdb", "admin", "admin");

            ResultSet rs = conn.getMetaData().getProcedureColumns(null, null, "testproc", null);

            System.out.println("Driver: " + conn.getMetaData().getDriverVersion());

            while (rs.next()) {

                System.out.println("Parameter Name: " + rs.getString(4) + " Paramter Type: " + rs.getShort(5) + " Data Type: " + rs.getInt(6));

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

Output with different drivers (all against PostgreSQL 9.1.11 server):

Driver: PostgreSQL 8.0 JDBC3 with SSL (build 313)
Parameter Name: returnValue Paramter Type: 5 Data Type: 1111
Parameter Name: $1 Paramter Type: 1 Data Type: 4

Driver: PostgreSQL 9.0 JDBC4 (build 801)
Parameter Name: itemid Paramter Type: 1 Data Type: 4
Parameter Name: id Paramter Type: 1 Data Type: 4
Parameter Name: name Paramter Type: 1 Data Type: 12

Driver: PostgreSQL 9.3 JDBC4 (build 1100)
Parameter Name: itemid Paramter Type: 1 Data Type: 4
Parameter Name: id Paramter Type: 1 Data Type: 4
Parameter Name: name Paramter Type: 1 Data Type: 12

Note that the 8.0 Driver flags the return value as Type 5 .. all other drivers flag the 2nd and 3rd paramters as Type 1.

Obviously the PostgreSQL 8 driver is JDBC3 and the others JDBC4. If this is the reason that the results are different then that's great.. but I still want to distinguish between an actual input paramter and output parameter.

1
Please be exact about your driver versions. Also, there's not really a "Postgresql 8" or "PostgreSQL 9", see the version policy postgresql.org/support/versioning . Do you mean 8.1, 8.2, 8.3, 8.4, 9.0, 9.1, 9.2, 9.3 ? - Craig Ringer
Versions are: PostgreSQL database that I am connecting to is 9.11. PostgreSQL "9" driver is latest 9.3, PostgreSQL 9.3 JDBC4 (1100). PostgreSQL "8" driver is PostgreSQL 8.0 JDBC3 with SSL (313). Have updated post with this information. - Sprooose
OK, so a very obsolete release works how you expect, a current one doesn't. Two things to do here: Put together a self contained, compileable test case (Java class, plus SQL setup script) - then use it to figure out in which driver release the behaviour changed. Post the self-contained test case here, it'll help illustrate the nature of the problem, and whether it's a JDBC compliance issue or not. - Craig Ringer
How do you query the metadata? - Mark Rotteveel
Metadata is queried using the JDBC driver.. Specifically the DatabaseMetaData.getProcedureColumns call.. docs.oracle.com/javase/7/docs/api/java/sql/… - Sprooose

1 Answers

2
votes

There was a issue in the PostgreSQL JDBC driver. Building the driver from the lastest PostgreSQL JDBC driver source code returned the correct meta-data for the Stored Procedure.

Driver: PostgreSQL 9.4 JDBC4.1 (build 1200)
Parameter Name: itemid Paramter Type: 1 Data Type: 4
Parameter Name: id Paramter Type: 5 Data Type: 4
Parameter Name: name Paramter Type: 5 Data Type: 12