Thread: Calling functions with table-based-type parametars

Calling functions with table-based-type parametars

From
Mario Splivalo
Date:
Hello again :)

I have a PSQL function that accepts table-based-type as parametar:

CREATE TABLE t1(c1 int4, c2 varchar);

CREATE FUNCTION f1(t1) RETURNS VOID
AS 'BEGIN RETURN END;' LANGUAGE 'plpgsql';

Now, when I call this function from another function (or from psql, for
instance), i need to do it like this:

select f1(ROW(1, 'sometext'));

How do I do that from JDBC? Usualy I did it like this:

callStatement = conn.getCallStatement("{call f1(?, ?)}");
callStatement.setInt(1, 1);
callStatement.setString(2, 'sometext');
callStatement.execute();
ResultSet rs = callStatement.getResultSet();

But when I do it like that I get 'function does not exists', which is
logical (my function accepts one, not two parametars).

I have found out that I can use createstatement like this:

Connection c = DriverManager.getConnection('jdbc://...');
s = c.createStatement();
ResultSet rs = s.executeQuery("select f1(ROW(1, 'sometext'));");

Now, as I understand, first snippet uses prepared statements, and second
one doesnt. Can I have prepared statements and still pass ROW as
function parametar? Or JDBC does not support that?

    Mike


Re: Calling functions with table-based-type parametars

From
Kris Jurka
Date:

On Fri, 23 Feb 2007, Mario Splivalo wrote:

> Hello again :)
>
> I have a PSQL function that accepts table-based-type as parametar:
>
> CREATE TABLE t1(c1 int4, c2 varchar);
>
> CREATE FUNCTION f1(t1) RETURNS VOID
> AS 'BEGIN RETURN END;' LANGUAGE 'plpgsql';
>
> Now, when I call this function from another function (or from psql, for
> instance), i need to do it like this:
>
> select f1(ROW(1, 'sometext'));
>
> How do I do that from JDBC? Usualy I did it like this:
>
> callStatement = conn.getCallStatement("{call f1(?, ?)}");
> ResultSet rs = s.executeQuery("select f1(ROW(1, 'sometext'));");
>

Why not conn.prepareCall("{call f1(ROW(?, ?)}") or
conn.prepareStatement("SELECT f1(ROW(?, ?))");

Technically the JDBC way would be to to create a class implementing
SQLData, but since the JDBC driver doesn't implement such a thing, one of
the above should work.

Kris Jurka


Re: Calling functions with table-based-type parametars

From
Mario Splivalo
Date:
On Fri, 2007-02-23 at 14:20 -0500, Kris Jurka wrote:
>
> On Fri, 23 Feb 2007, Mario Splivalo wrote:
>
> > Hello again :)
> >
> > I have a PSQL function that accepts table-based-type as parametar:
> >
> > CREATE TABLE t1(c1 int4, c2 varchar);
> >
> > CREATE FUNCTION f1(t1) RETURNS VOID
> > AS 'BEGIN RETURN END;' LANGUAGE 'plpgsql';
> >
> > Now, when I call this function from another function (or from psql, for
> > instance), i need to do it like this:
> >
> > select f1(ROW(1, 'sometext'));
> >
> > How do I do that from JDBC? Usualy I did it like this:
> >
> > callStatement = conn.getCallStatement("{call f1(?, ?)}");
> > ResultSet rs = s.executeQuery("select f1(ROW(1, 'sometext'));");
> >
>
> Why not conn.prepareCall("{call f1(ROW(?, ?)}") or
> conn.prepareStatement("SELECT f1(ROW(?, ?))");

When I try it like above (using conn.prepaleCall), i get this:

2007-02-26 16:58:19.004 CET [9592] <jura> SELECTERROR:  function
f1(record) does not exist
2007-02-26 16:58:19.004 CET [9592] <jura> SELECTHINT:  No function
matches the given name and argument types. You may need to add explicit
type casts.

I know I can't but I still tried creating function with record-type
parametar, postgres won't let me do so.

I tried SELECT f1(ROW(1, 'one'));, that works. If I try it like this:
SELECT f1(ROW(1, 'one', 'two)), postgres tells me I have to many
columns, and it can't cast to t1 type.

If, in Java code, i do my call like this:

conn.prepareCall("{call f1(ROW(?, ?, ?)}"

and set all three parametars, I still get 'function f1(record) does not
exist', no complaints about missing or to many columns.

I'm using pg7.4 JDBC driver with pg8.1.2, and I tought that might be the
problem. But I switched to postgresql-8.1-407.jdbc3.jar, and I still get
the same error.

    Mike


Re: Calling functions with table-based-type parametars

From
Kris Jurka
Date:

On Mon, 26 Feb 2007, Mario Splivalo wrote:

>> Why not conn.prepareCall("{call f1(ROW(?, ?)}") or
>> conn.prepareStatement("SELECT f1(ROW(?, ?))");
>
> When I try it like above (using conn.prepaleCall), i get this:
>
> 2007-02-26 16:58:19.004 CET [9592] <jura> SELECTERROR:  function
> f1(record) does not exist
> 2007-02-26 16:58:19.004 CET [9592] <jura> SELECTHINT:  No function
> matches the given name and argument types. You may need to add explicit
> type casts.

This is saying you need to write it with a cast from the row to the table
type:

conn.prepareCall("{call f1(ROW(?, ?)::t1)}")

Kris Jurka


Re: Calling functions with table-based-type parametars

From
Kris Jurka
Date:
Mario Splivalo wrote:
>> This is saying you need to write it with a cast from the row to the table
>> type:
>>
>> conn.prepareCall("{call f1(ROW(?, ?)::t1)}")
>
> Can't do that either. Postgres tells me that there is no type t1:
>
> 2007-02-26 18:25:19.004 CET [10324] <jura> PARSEERROR: type "t1" does
> not exist
>
> I even tried creating type _t1 wich has same member variables as table
> rows, and created function f2 wich takes _t1 as parametar, still same
> error: type "_t1" does not exsit.
>

The attached test case works fine for me without a cast.  Perhaps you
can modify this to show the failure you're getting.

Kris Jurka
import java.sql.*;

public class RowFunc {

    public static void main(String args[]) throws Exception {
        Class.forName("org.postgresql.Driver");
        Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/jurka","jurka","");

        Statement stmt = conn.createStatement();
        try {
            stmt.execute("DROP FUNCTION f1(t1)");
        } catch (SQLException sqle) {
        }
        try {
            stmt.execute("DROP TABLE t1");
        } catch (SQLException sqle) {
        }
        stmt.execute("CREATE TABLE t1 (a int, b int)");
        stmt.execute("CREATE FUNCTION f1(t1) RETURNS int AS 'SELECT 1' language 'SQL'");
        stmt.close();

        CallableStatement cs = conn.prepareCall("{? = call f1(ROW(?,?))}");
        cs.registerOutParameter(1, Types.INTEGER);
        cs.setInt(2,2);
        cs.setInt(3,3);
        cs.execute();

        System.out.println(cs.getInt(1));
    }
}



Re: Calling functions with table-based-type parametars

From
Mario Splivalo
Date:
On Mon, 2007-02-26 at 10:53 -0700, Kris Jurka wrote:
> Mario Splivalo wrote:
> >> This is saying you need to write it with a cast from the row to the table
> >> type:
> >>
> >> conn.prepareCall("{call f1(ROW(?, ?)::t1)}")
> >
> > Can't do that either. Postgres tells me that there is no type t1:
> >
> > 2007-02-26 18:25:19.004 CET [10324] <jura> PARSEERROR: type "t1" does
> > not exist
> >
> > I even tried creating type _t1 wich has same member variables as table
> > rows, and created function f2 wich takes _t1 as parametar, still same
> > error: type "_t1" does not exsit.
> >
>
> The attached test case works fine for me without a cast.  Perhaps you
> can modify this to show the failure you're getting.
>

As it turns out, it was permissions problem. I created the table and the
function as the database owner. But, there is user set up for
tomcat/jdbc, and all data manipulation is done trough psql functions.
Now, that user has GRANT EXECUTE ON f1(t1) TO jdbcuser;

Still, when I try to SELECT f1 from psql, connected as uset jdbcuser, I
get the ERROR: function f1(record) does not exist.

If I try it with dbowner user, everything works fine. Now I don't know
how to set up permisions, but that's for another mailing list.

Thank you for your effort, Kris.

    Mario


Re: Calling functions with table-based-type parametars

From
Mario Splivalo
Date:
On Tue, 2007-02-27 at 13:24 +0100, Mario Splivalo wrote:

> As it turns out, it was permissions problem. I created the table and the
> function as the database owner. But, there is user set up for
> tomcat/jdbc, and all data manipulation is done trough psql functions.
> Now, that user has GRANT EXECUTE ON f1(t1) TO jdbcuser;

I forgot to GRANT USAGE on schema public for jdbcuser. Now it's ok.

    Mike