No data output from stored procedure (Postgresql) - sql

I have a basic stored procedure for a select statement. The select statement by itself works and shows me the data output, but when I try calling it from a stored procedure, it says 'CALL Query returned successfully in 107 msec', but there's no data output. Is there something that I'm missing from my stored procedure?
(I'm also connecting a database on AWS with the basic tier, not sure if that makes a difference. Every other CRUD operation works except for select.)
CREATE PROCEDURE
experiment1()
LANGUAGE SQL
AS $$
SELECT * FROM assignment
$$

A Postgres procedure does not return anything. You can use a function instead, with the return query syntax. This requires enumerating the columns that the query returns. Assuming that your table has two columns, id and val, that would be:
create function experiment()
returns table (id int, val text)
as $$
begin
return query select * from assignment;
end;
$$ language plpgsql;
You can then invoke this set-returning function like so:
select * from experiment();
Demo on DB Fiddle:
create table assignment (id int, val text);
insert into assignment values(1, 'foo'), (2, 'bar');
-- 2 rows affected
create function experiment()
returns table (id int, val text)
as $$
begin
return query select * from assignment;
end;
$$ language plpgsql;
select * from experiment();
id | val
-: | :--
1 | foo
2 | bar

Related

Creating stored procedure in redshift

When creating stored procedure in redshift like below:
CREATE OR REPLACE PROCEDURE sp_test()
AS '
BEGIN
TRUNCATE TABLE TABLE_1;
INSERT INTO TABLE_1
SELECT COL1, COL2
FROM TABLE_2
WHERE CONDITION='SAMPLE';
END;
'
LANGUAGE plpgsql;
This gives an error syntax error near 'SAMPLE' because single quotes is already used for stored procedure begin and end. Also, here we would not be able to replace single quotes in INSERT query to double because redshift will consider it to be a column.
Few other posts suggests to use $$ for stored procedure, however $$ is not supported in sql workbench.
Any work around for this. Thanks.
Have you tried double-quoting the string?
WHERE CONDITION=''SAMPLE'';
Data Sample
CREATE TABLE t (id int, status text);
INSERT INTO t VALUES (42,'foo');
Procedure
CREATE OR REPLACE PROCEDURE sp_test()
AS'
BEGIN
TRUNCATE TABLE t;
INSERT INTO t
SELECT 8,''new record'';
END;'
LANGUAGE plpgsql;
Test procedure
CALL sp_test();
SELECT * FROM t
id | status
----+------------
8 | new record
(1 Zeile)

Returning dynamic SQL statement in PL/pgSQL Functions

I have a table called "points" with a column called "geom" of type geometry.
I want to create a function that returns a table with a column of "geometry" data type. I have been successful in returning a table with my the correct data type when the name of the target table (points) is hardcoded in the "RETURN QUERY" clause.
I want to have the name of the table as an input of the function (in a dynamic way). How can I change this code to accept the name of the target table (called points in this code) as an input?
CREATE OR REPLACE FUNCTION milad_points()
RETURNS TABLE (geom points.geom%TYPE)
AS $$
BEGIN
RETURN QUERY SELECT points.geom FROM points;
END;
$$ LANGUAGE PLPGSQL;
I know that for managing the dynamic queries we have to make it as a string and run it as EXECUTE sql_string. However, I could not get it work in the above-mentioned example.
The only way to do something like that is to use the anyelement data type.
But in order to use anyelement as a return type, you have to specify an anyelement parameter too. It is not important what value you use as argument, but its data type determines the actual data type returned.
See the following example:
CREATE FUNCTION anyfun(tabname name, typdef anyelement) RETURNS SETOF anyelement
LANGUAGE plpgsql STABLE AS
$$BEGIN
RETURN QUERY EXECUTE format('SELECT id FROM %I', tabname);
END;$$;
Now let's test it with two different tables:
CREATE TABLE anytab (id integer);
INSERT INTO anytab VALUES (1), (42);
SELECT * FROM anyfun('anytab', NULL::integer);
anyfun
--------
1
42
(2 rows)
CREATE TABLE anothertab (id text);
INSERT INTO anothertab VALUES ('one'), ('two');
SELECT * FROM anyfun('anothertab', NULL::text);
anyfun
--------
one
two
(2 rows)

PostgreSQL - Shared temp table between functions

I wnat to know if its possible to share a temporary table between functions that are called in a "main function", like this:
-- some sub function
create or replace function up_sub_function (str text)
returns table (id int, descr text) as $$
begin
return query select * from temp_table where descr like concat('%', str , '%');
end; $$
language plpgsql;
-- main function
create or replace function up_main_function ()
returns table (id int, descr text) as $$
begin
create temporary table temp_table if not exists (
id int,
descr text
);
insert into temp_campaigns select id, descr from test_table;
return query select * from up_sub_function('a');
end; $$
language plpgsql;
BEGIN;
select * from up_main_function();
drop table temp_table;
COMMIT;
If you can show me the correct way to achieve this, I want to be able to populate a temporary table and then filter rows by calling othe functions inside the main function.
Thanks ans happy programming! :)
See the documentation https://www.postgresql.org/docs/current/static/sql-createtable.html
temp tables are valid for the entire session. That is as long as you stay connected to the database.
In your case you only need it during the transaction. So you should create it with ON COMMIT DROP
create temporary table temp_table if not exists (
id int,
descr text
) ON COMMIT DROP;
Once you created the table you can use it within any function in the current transaction.
You do not need the BEGIN to start the transaction. A transaction is automatically started when the outer function is called.
Nested function calls share the same transaction. So they all see the table.

SQL function return-type: TABLE vs SETOF records

What's the difference between a function that returns TABLE vs SETOF records, all else equal.
CREATE FUNCTION events_by_type_1(text) RETURNS TABLE(id bigint, name text) AS $$
SELECT id, name FROM events WHERE type = $1;
$$ LANGUAGE SQL STABLE;
CREATE FUNCTION events_by_type_2(text) RETURNS SETOF record AS $$
SELECT id, name FROM events WHERE type = $1;
$$ LANGUAGE SQL STABLE;
These functions seem to return the same results. See this SQLFiddle.
When returning SETOF record the output columns are not typed and not named. Thus this form can't be used directly in a FROM clause as if it was a subquery or a table.
That is, when issuing:
SELECT * from events_by_type_2('social');
we get this error:
ERROR: a column definition list is required for functions returning
"record"
It can be "casted" into the correct column types by the SQL caller though. This form does work:
SELECT * from events_by_type_2('social') as (id bigint, name text);
and results in:
id | name
----+----------------
1 | Dance Party
2 | Happy Hour
...
For this reason SETOF record is considered less practical. It should be used only when the column types of the results are not known in advance.
This answer is only to remember alternative context where TABLE and SETOF are equivalent.
As #a_horse_with_no_name pointed, it is not a RETURNS SETOF "unknown record", is a defined one.
In this example, the types table and setof are equivalent,
CREATE TYPE footype AS (score int, term text);
CREATE FUNCTION foo() RETURNS SETOF footype AS $$
SELECT * FROM ( VALUES (1,'hello!'), (2,'Bye') ) t;
$$ language SQL immutable;
CREATE FUNCTION foo_tab() RETURNS TABLE (score int, term text) AS $$
SELECT * FROM ( VALUES (1,'hello!'), (2,'Bye') ) t;
$$ language SQL immutable;
SELECT * FROM foo(); -- works fine!
SELECT * FROM foo_tab(); -- works fine and is equivalent.
The RETURNS SETOF have the advantage of reuse type (see footype), that is impossible with RETURNS TABLE.

Passing a ResultSet into a Postgresql Function

Is it possible to pass the results of a postgres query as an input into another function?
As a very contrived example, say I have one query like
SELECT id, name
FROM users
LIMIT 50
and I want to create a function my_function that takes the resultset of the first query and returns the minimum id. Is this possible in pl/pgsql?
SELECT my_function(SELECT id, name FROM Users LIMIT 50); --returns 50
You could use a cursor, but that very impractical for computing a minimum.
I would use a temporary table for that purpose, and pass the table name for use in dynamic SQL:
CREATE OR REPLACE FUNCTION f_min_id(_tbl regclass, OUT min_id int) AS
$func$
BEGIN
EXECUTE 'SELECT min(id) FROM ' || _tbl
INTO min_id;
END
$func$ LANGUAGE plpgsql;
Call:
CREATE TEMP TABLE foo ON COMMIT DROP AS
SELECT id, name
FROM users
LIMIT 50;
SELECT f_min_id('foo');
Major points
The first parameter is of type regclass to prevent SQL injection. More info in this related answer on dba.SE.
I made the temp table ON COMMIT DROP to limit its lifetime to the current transaction. May or may not be what you want.
You can extend this example to take more parameters. Search for code examples for dynamic SQL with EXECUTE.
-> SQLfiddle demo
I would take the problem on the other side, calling an aggregate function for each record of the result set. It's not as flexible but can gives you an hint to work on.
As an exemple to follow your sample problem:
CREATE OR REPLACE FUNCTION myMin ( int,int ) RETURNS int AS $$
SELECT CASE WHEN $1 < $2 THEN $1 ELSE $2 END;
$$ LANGUAGE SQL STRICT IMMUTABLE;
CREATE AGGREGATE my_function ( int ) (
SFUNC = myMin, STYPE = int, INITCOND = 2147483647 --maxint
);
SELECT my_function(id) from (SELECT * FROM Users LIMIT 50) x;
It is not possible to pass an array of generic type RECORD to a plpgsql function which is essentially what you are trying to do.
What you can do is pass in an array of a specific user defined TYPE or of a particular table row type. In the example below you could also swap out the argument data type for the table name users[] (though this would obviously mean getting all data in the users table row).
CREATE TYPE trivial {
"ID" integer,
"NAME" text
}
CREATE OR REPLACE FUNCTION trivial_func(data trivial[])
RETURNS integer AS
$BODY$
DECLARE
BEGIN
--Implementation here using data
return 1;
END$BODY$
LANGUAGE 'plpgsql' VOLATILE;
I think there's no way to pass recordset or table into function (but I'd be glad if i'm wrong). Best I could suggest is to pass array:
create or replace function my_function(data int[])
returns int
as
$$
select min(x) from unnest(data) as x
$$
language SQL;
sql fiddle demo