Using multi-column SETOF result from a PL/pgSQL function in via jdbc - sql

When executing a PL/pgSQL function returning a result of a query, Postgresql jdbc driver seems to return only a single column ResultSet. I cannot figure out how to access the members of the "tuple".
For example, if I have a database
CREATE TABLE things (id SERIAL PRIMARY KEY, name VARCHAR);
INSERT INTO things (name) VALUES ('pen'), ('eraser');
-- function to return all rows as a SETOF
CREATE OR REPLACE FUNCTION list_things () RETURNS SETOF things AS $$
BEGIN
RETURN QUERY SELECT id, name FROM things;
END
$$ LANGUAGE PLPGSQL;
and I use the PL/pgSQL function list_things() from Java like this:
static void printThings(Connection c) throws SQLException {
Statement s = c.createStatement();
String sql = "SELECT list_things()";
ResultSet rs = s.getResultSet();
while (rs.next()) {
// is String all I can make of it?
System.out.println(rs.getString(1));
// how can I access the name member of result row?
}
}
The output looks like
(1,pen)
(2,eraser)
How can I access the members of tuples other than by parsing the string?

change
SELECT list_things()
to
SELECT * from list_things()
so it would return rows as they are and not as one tuple with row type

Related

Reference a parameter in Postgres function

The table org has a column called npi. Why does query1 work and query2 not?
Query 1 -
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
DECLARE
pass_npi TEXT;
BEGIN
pass_npi := npi;
SELECT 1
FROM org doc
WHERE doc.npi = pass_npi
;
RETURN 1;
END $$
Query 2 -
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
BEGIN
SELECT 1
FROM org doc
WHERE doc.npi = npi
;
RETURN 1;
END $$
ERROR -
Ambigious column name NPI
Because in the second case it is unclear if npi is the table column (that would be a valid, if useless statement) or the function parameter.
There are three solutions apart from the one in your first query:
The best one: use function parameters that have names different from table columns. This can be done by using a prefix:
CREATE FUNCTION check (p_npi TEXT) RETURNS boolean AS
...
SELECT ...
WHERE doc.npi = p_npi
Use the ALIAS command to “rename” the parameter:
CREATE FUNCTION check (npi TEXT) RETURNS boolean AS
$$DECLARE
p_npi ALIAS FOR npi;
BEGIN
...
SELECT ...
WHERE doc.npi = p_npi
Qualify the parameter with the function name:
CREATE FUNCTION check (npi TEXT) RETURNS boolean AS
...
SELECT ...
WHERE doc.npi = check.npi
What happens is that in Query2 you are comparing the field the doc.npi field with it, it is the same to say doc.npi and to say npi, for that reason it shows you that the sentence is ambiguous, on the contrary case in Query1 you are comparing the doc.npi field with a different field that is the pass_npi.
To solve this problem you must compare the same columns but from different tables or different columns from the same table.
Query2:
CREATE OR REPLACE FUNCTION check (npi TEXT)
RETURNS BOOLEAN AS $$
BEGIN
SELECT 1
FROM org doc
WHERE doc.npi = pass_npi
;
RETURN 1;
END $$

Returning table with specific parameters from a PostgreSQL function

I have this function, with which I would like to return a table, which holds two columns: game_name and follow (this would be an integer 0 or 1):
CREATE OR REPLACE FUNCTION public.toggle2(uid numeric, gid NUMERIC)
RETURNS TABLE (
follow INT,
game_name TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
IF NOT EXISTS(SELECT *
FROM game_follows
WHERE user_id = uid and game_id = gid)
THEN
INSERT INTO game_follows(user_id, game_id) VALUES(uid, gid);
follow := 1;
ELSE
DELETE FROM game_follows WHERE user_id = uid and game_id = gid;
follow := 0;
END IF;
SELECT name INTO game_name FROM games WHERE id = gid;
END;
$$
;
Sadly, the function returns empty values. I am using it as this:
SELECT * FROM toggle2(83, 12);
A function declared to RETURN TABLE can return 0-n rows.
You must actively return rows, or nothing will be returned (no row). One way to do this:
RETURN NEXT; -- as last line before END;
There are other ways, see the manual.
However, it seems you want to return exactly one row every time. So rather use OUT parameters:
CREATE OR REPLACE FUNCTION public toggle2(uid numeric, gid numeric, OUT follow int, OUT game_name text) AS ...
Then it's enough to assign those OUT parameters, they are returned in the single result row automatically.
See:
Returning from a function with OUT parameter
plpgsql error "RETURN NEXT cannot have a parameter in function with OUT parameters" in table-returning function
How to return result of a SELECT inside a function in PostgreSQL?

Return a table using SQL Procedure?

I have problem with return table using SQL Procedure.
My code:
CREATE PROCEDURE return_data(surname character varying)
LANGUAGE SQL
AS $$
SELECT * FROM peopple WHERE surname=surname
$$;
CALL return_data('Jobs');
Currently the procedure executes without error but it doesn't return a table.
Procedures aren't meant to return anything. Use a set returning function
create function return_data(p_surname varchar)
returns setof people
as
$$
select *
from people
where surname = p_surname;
$$
language sql;
You should also avoid parameter or variable names that have the same name as a column.
Then use it like this:
select *
from return_data('Jobs');
You can append one or more rows to the result set:
CREATE OR REPLACE FUNCTION return_data(surname character varying)
RETURNS TABLE(firstname character varying, surname character varying, age integer)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT firstname, surname, age
FROM users;
END;
$function$
;
Invoke the function:
SELECT * FROM return_data("a surname");
A function can return but a procedure cannot. Thats true, I wanna add to that we generally use function to return values of one type (in general one value)
While,
Procedure never returns but can behave like a function without a return statement using OUT parameters. To add to this we use procedure when we dont wanna return in actual but executing sql statements but in case if we require a procedure to give multiple values we can make procedure behave like a function for multole values output vja OUT.
As a table can be accessed by record or array type depending upon homogenity. Therefore, use function with return type as record/array.
Hence, as function is solely used to return single output we have to specify via return.

"ERROR: query has no destination for result data"

I am trying to create a function like this:
I tried changing the return type to int or text etc. to see if the code works outside of that, but it doesn't. I am a beginner in PostgreSQL so please don't be harsh if I missed something obvious.
create or replace function date_select(i INT) returns void as
$$
begin
select * from dwh_stg.stg_dce_gift where gift_id = i;
end
$$ language plpgsql
select date_select(16940)
SQL Error [42601]:
ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function date_select(integer) line 3 at SQL statement
If you want to return something, you need to define the function to return something (not void)
Apparently you want to return multiple rows from the table stg_dec_gift, for that you need to define the function as returns setof dwh_stg.stg_dce_gift. For a simple function encapsulating a query there is not need to use PL/pgSQL a plain SQL function will do just fine:
create or replace function date_select(i INT)
returns setof dwh_stg.stg_dce_gift --<< here
as
$$
select *
from dwh_stg.stg_dce_gift
where gift_id = i;
$$
stable
language sql;
Then use it in the FROM part:
select *
from date_select(16940);
Online example: https://rextester.com/WYDCE44062

Multiple JDBC ResultSets from PostgreSQL Function

I'd like to write a PLPGSQL (targeting PostgreSQL 9.3) function which will return multiple result sets (i.e. when accessed through JDBC I'll call getMoreResults() to move to the next set of rows), but everything I've tried either gives me a syntax error or simply concatenates everything together into a single result set.
Here is a simple example that illustrates the issue:
CREATE TYPE my_type AS (a BIGINT, b TEXT, c DOUBLE PRECISION);
CREATE FUNCTION my_func(_arg1 TEXT, _arg2 TEXT)
RETURNS SETOF my_type
AS $$
BEGIN
RETURN QUERY SELECT a, b, c FROM table1 WHERE d = _arg1 AND e = _arg2;
RETURN QUERY SELECT a, b, c FROM table2 WHERE d = _arg1 AND e = _arg2;
END;
$$ LANGUAGE PLPGSQL;
When I run this the rows from both table1 and table2 get concatenated together into a single result set.
I've also tried something like
CREATE FUNCTION my_func(_arg1 TEXT, _arg2 TEXT)
RETURNS SETOF TABLE(a BIGINT, b TEXT, c DOUBLE PRECISION)
But this just results in a cryptic syntax error: ERROR: syntax error at end of input.
For completeness sake here is the Java code I'd like to use to process the results. I'm fairly certain the issue is on the db function side, but it's possible I'm misunderstanding how the JDBC API is supposed to work.
(Error handling and resource closing removed for readability)
PreparedStatement statement = connection.prepareStatement("SELECT * FROM my_func(?, ?);");
statement.setString(1, "foo");
statement.setString(2, "bar");
statement.execute();
ResultSet rs1 = statement.getResultSet();
while(rs1.next()) {
// Process first result set
}
statement.getMoreResults();
ResultSet rs2 = statement.getResultSet();
while(rs2.next()) {
// Process second result set
}
Based on the searching I've done so far it seems like this type of solution is supposed to be supported by JDBC and PostgreSQL, but I can't find any explicit examples of it in action.
The only way you can get multiple resultsets from PostgreSQL (at time of writing - current as of PostgreSQL 9.4) is by returning refcursors.
Define your function RETURNS SETOF refcursor then return a cursor for each resultset. PgJDBC will recognise that it's a refcursor and fetch it for you; see the documentation.
Slightly modifying your question, but if you don't have to use a function, you can get multiple ResultSets from PostgreSQL exactly as you normally would with JDBC. Just concatenate your queries and process each result set using getMoreResults(). See Queries returning multiple result sets.