Question

I can't seem to read a UDT properly from a stored function with the postgres JDBC driver. This is some sample code:

CREATE TYPE u_country AS ENUM ('Brazil', 'England', 'Germany')

CREATE TYPE u_street_type AS (
  street VARCHAR(100),
  no VARCHAR(30)
)

CREATE TYPE u_address_type AS (
  street u_street_type,
  zip VARCHAR(50),
  city VARCHAR(50),
  country u_country,
  since DATE,
  code INTEGER
)

CREATE TABLE t_author (
  id INTEGER NOT NULL PRIMARY KEY,
  first_name VARCHAR(50),
  last_name VARCHAR(50) NOT NULL,
  date_of_birth DATE,
  year_of_birth INTEGER,
  address u_address_type
)

INSERT INTO t_author VALUES (1, 'George', 'Orwell',
TO_DATE('1903-06-25', 'YYYY-MM-DD'), 1903, ROW(ROW('Parliament Hill',
'77'), 'NW31A9', 'Hampstead', 'England', '1980-01-01', null))
INSERT INTO t_author VALUES (2, 'Paulo', 'Coelho',
TO_DATE('1947-08-24', 'YYYY-MM-DD'), 1947, ROW(ROW('Caixa Postal',
'43.003'), null, 'Rio de Janeiro', 'Brazil', '1940-01-01', 2))

CREATE FUNCTION p_enhance_address2 (address OUT u_address_type)
AS $$
BEGIN
        SELECT t_author.address
        INTO address
        FROM t_author
        WHERE first_name = 'George';
END;
$$ LANGUAGE plpgsql;

Now the above works perfectly in postgres. I can also select the UDT column t_author.address with a SQL SELECT statement directly. But when I select from the stored function p_enhance_address2 via JDBC, I get a weird behaviour. I tried these two invocation schemes:

connection.prepareStatement("select * from p_enhance_address2()");
connection.prepareCall("{ call p_enhance_address2(?) }"); 
// the latter with an output parameter registered

Both calling schemes induce the same behaviour (actually the CallableStatement is nothing else than selecting from the function). There seem to be two very distinct problems:

The nested UDT structure completely screws up fetching results. This is what I get with JDBC:

PreparedStatement stmt = connection.prepareStatement(
  "select * from p_enhance_address2()");
ResultSet rs = stmt.executeQuery();

while (rs.next()) {
  System.out.println("# of columns: " + 
    rs.getMetaData().getColumnCount());
  System.out.println(rs.getObject(1));
}

Output:

nr of columns: 6 ("(""Parliament Hill"",77)",NW31A9)

Why are there 6 columns? And why is the UDT incorrectly fetched (many fields are missing)

A little improvement can be achieved, when the nested UDT u_street_type is "flattened" to a varchar, which leads to the assumption that nested UDT's are poorly supported by the JDBC driver:

CREATE TYPE u_address_type AS (
  street VARCHAR(80),
  zip VARCHAR(50),
  city VARCHAR(50),
  country u_country,
  since DATE,
  code INTEGER
)

INSERT INTO t_author VALUES (1, 'George', 'Orwell',
TO_DATE('1903-06-25', 'YYYY-MM-DD'), 1903, ROW('Parliament Hill 77',
'NW31A9', 'Hampstead', 'England', '1980-01-01', null))
INSERT INTO t_author VALUES (2, 'Paulo', 'Coelho',
TO_DATE('1947-08-24', 'YYYY-MM-DD'), 1947, ROW('Caixa Postal 43.003',
null, 'Rio de Janeiro', 'Brazil', '1940-01-01', 2))

Then the results will be something like this:

nr of columns: 6 ("Parliament Hill 77",NW31A9,Hampstead,England,1980-01-01,)

The UDT record now looks correct (fetched from the result set at position 1). But there are still 6 columns in the result set.

Some facts:

  • I do not experience these problems in pgAdmin III
  • I use PostgreSQL 9.0.1, compiled by Visual C++ build 1500, 64-bit
  • I use postgresql-9.0-801.jdbc4.jar

Does anyone have any idea what's wrong?

Was it helpful?

Solution

I can reproduce this and it seems that this is a bug.

I would suggest you post this to the PostgreSQL JDBC mailing list, so the developers can fix this.

OTHER TIPS

If am not wrong UDT (user defined type) not officially supported (JDBC conformance) by postgres9.4 drivers (postgresql-9.0-801.jdbc4.jar). I also face lot of problem while calling postgres function form java code. I used below drivers and solved my problem.

here is the non official driver link, http://impossibl.github.io/pgjdbc-ng/ copy driver form the link create connection as explained in below

please see below working example from my POC Oracle10g to Postgres9.4 migration activity see below example POC,

-- Function: fn_test_t(text)

-- DROP FUNCTION fn_test_t(text);

CREATE OR REPLACE FUNCTION fn_test_t(
    IN txt text,
    OUT a typ_address[],
    OUT b typ_address[])
  RETURNS record AS
$BODY$
DECLARE 
address typ_address[];
BEGIN
RAISE INFO '@@ inside fn_test:(%)',111;
address[1] := ROW('Amravati', 'Mahalaxmi', ROW('Test1'));
address[2] := ROW('Pune', 'NICMAR',ROW('Test2'));
address[3] := ROW('', '',ROW('Test3'));
RAISE INFO 'array of address:(%)',address;
--a := ROW(address); 
--b := ROW(address); 
a := address; 
b := address; 
RAISE INFO 'typ_address_t a:(%)',a;
RAISE INFO 'typ_address_t b:(%)',b;
  --  RETURN address;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION fn_test_t(text)
  OWNER TO postgres;

=========================
-- Type: typ_address

-- DROP TYPE typ_address;

CREATE TYPE typ_address AS
   (add1 character varying,
    add2 character varying,
    t typ_test);
ALTER TYPE typ_address
  OWNER TO postgres;
==========================
-- Type: typ_test

-- DROP TYPE typ_test;

CREATE TYPE typ_test AS
   (t1 character varying);
ALTER TYPE typ_test
  OWNER TO postgres;
==========================

Main function call,

public static void oracleToPosgresUDTCall() {
        System.out.println("@@@ inside oracleToPosgresUDTCall...");
          Connection c = null;
          try {
             Class.forName("com.impossibl.postgres.jdbc.PGDriver");
             c = DriverManager
                .getConnection("jdbc:pgsql://localhost:5433/orapg", "postgres", "root");
             System.out.println(c.getMetaData().getDriverVersion());

             //you can map your UDT to pojo here Great !!!
             Map<String, Class<?>> m = c.getTypeMap();
             m.put("typ_address", Address.class);
             m.put("typ_test", AddressTypeTest.class);
             c.setTypeMap(m);

             // Procedure call
             CallableStatement cstmt = c.prepareCall("{call fn_test_t(?,?,?)}");

             cstmt.setString(1, "791000252423");

             cstmt.registerOutParameter(2, Types.ARRAY);
             cstmt.registerOutParameter(3, Types.ARRAY);


                boolean b = cstmt.execute();

             Array arr = cstmt.getArray(1); //output array starts from index 1

             System.out.println("arr:" + arr.getBaseTypeName());
             Object obj = arr.getArray();

             System.out.println("Address obj:" + obj);
             Address[] a = (Address[])obj;
             System.out.println("Address obj:" + a[0].getAdd1());
             System.out.println("Address obj:" + a[0].getTypeTest().getT1());

             System.out.println("=======================================================");
             //MORE

             List<Address> list = Arrays.asList(a);

             for(Address aa: list){
                 System.out.println(aa.getAdd1());
                 System.out.println(aa.getAdd2());
                 System.out.println("t1:" + aa.getTypeTest().getT1());
             }



             cstmt.close();

          } catch (Exception e) {
             e.printStackTrace();
             System.err.println(e.getClass().getName()+": "+e.getMessage());
             System.exit(0);
          }
          System.out.println("Opened database successfully");
       }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top