Writing a Procedure in PostgreSQL 9.1 - sql

I am trying to write a procedure (function) in Postgres 9.1 to just select all data from a view and return it (all columns) so our application can use it. The intent is to have the application make a call to the procedure whenever it wants the data. My problem is that I cannot seem to get the syntax right to have it return any data. I have written procedures in MySQL, SQL Server and Oracle but never in postgres and it is quite different. Once it works is the best way to call it to just use select "function-name"? Any help would be appreciated.
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record AS
$BODY$
declare times record;
begin
select * into times from fg.fn_dy_dest_dep_arr;
return;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION fg.get_flight_times()
OWNER TO jason;
GRANT EXECUTE ON FUNCTION fg.get_flight_times() TO fltrakr;

Inside a PL/pgSQL function you need to use RETURN NEXT or RETURN QUERY as documented in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#AEN54092
User RETURNS setof record is no longer necessary in 9.1, using returns table is a lot easier. If you only want to return the result of SELECT statement, you can use SQL as well, which makes the function even easier:
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS table (col1 integer, col2 text, col3 text)
$BODY$
select col1, col2, col3 from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
Note that in this case, you have to specify the column definition returned by your function at creation time. With that version you just use:
select * from fg.get_flight_times();
When using returns setof record you don't need to specify the columns at creation time, but you will be forced to do so when selecting from the function (which I find more complicated, as this needs to be done everytime the function is used):
CREATE OR REPLACE FUNCTION fg.get_flight_times()
RETURNS setof record
$BODY$
select * from fg.fn_dy_dest_dep_arr;
$BODY$
LANGUAGE sql
VOLATILE
COST 100;
And this is used like this:
select *
from fg.get_flight_times() f(col1 integer, col2 text, col3 text);
Much more complicated in my opinion (because the caller needs to know the structure and datatypes of the columns)
Do read the manual, everything I have written here is documented there as well (I know it only because I have already read the manual...)

Related

POSTGRESQL How to return multiple rows from SQL function?

I'm new to databases and SQL and i've been messing around with functions on POSTGRESQL and I made a simple function to select all names from a table.
But the function, when called, returns just a single row as opposed to all rows being returned when i use SELECT c_name FROM customers; instead of the code shown below:
CREATE OR REPLACE FUNCTION get_cust_names()
RETURNS varchar AS
$body$
SELECT c_name FROM customers;
$body$
LANGUAGE SQL
Function call
SELECT get_cust_names()
this returns just a SINGLE row and this isnt code that i will use in a project etc. I'm just curious as to why postgresql behaves this way.
You need to declare the function as returns table(..) or returns setof
CREATE OR REPLACE FUNCTION get_cust_names()
RETURNS table(cust_name varchar)
AS
$body$
SELECT c_name
FROM customers;
$body$
LANGUAGE SQL;
Then use it like a table:
select *
from get_cust_names();

I'm trying to cross database with insert query in plpgsql, please help me

CREATE OR REPLACE FUNCTION "public"."cross_insert"("p_name" varchar, "p_detail" varchar)
RETURNS SETOF "pg_catalog"."varchar" AS $BODY$
BEGIN
SELECT * FROM public.dblink(
'
host=10.10.10.53
port=5432
user=sassuperuser
password=password10
dbname=blog2
',
'
SELECT * FROM public.funct_insert2(
'''||p_name||''',
'''||p_detail||'''
);
'
);
RETURN query
SELECT ('SUKSES')::character varying;
END$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000
Your code is little bit messy. Try start with reading documentation to PLpgSQL. There are three issues:
PL/pgSQL doesn't allow implicit throwing result of any query. You should to iterate over result (FOR IN SELECT) or you have to save result to variable (SELECT INTO). If you want to do it, then you should to use PERFORM command. So you query should to look like:
PERFORM public.dblink(' .....');
There is not any sense why your function is declared like RETURNS SETOF varchar. Then you have to use RETURN QUERY SELECT 'success'. It is absolutely useless. There is relatively high overhead of this functionality. This should be classic scalar function that returns text or better, that returns void type. The exception is raised on error. So you don't need to returns anything.
Second, don't do this - PostgreSQL has FDW interface. You can use foreign table instead. It will be faster and safer.

Get table-name from an arbitrary %ROWTYPE or RECORD value

Desired setup
To improve the elegance of some code, I was wondering whether it was/is possible to do the opposite of %ROWTYPE variable from table name — extract a table's name from a %ROWTYPE or RECORD (most relevantly, after it's been passed into a function which accepts the ANYELEMENT pseudo-type):
CREATE OR REPLACE FUNCTION generic_function(row_data ANYELEMENT)
RETURNS INT AS $$
DECLARE
table_name TEXT = get_table_name(row_data);
BEGIN
-- [ work goes here ]
END;
$$ LANGUAGE plpgsql;
Current workaround
CREATE OR REPLACE FUNCTION generic_function(row_data ANYELEMENT, table_name TEXT)
RETURNS INT AS $$
BEGIN
-- [ work goes here ]
END;
$$ LANGUAGE plpgsql;
The workaround feels a touch redundant, if you can imagine how it might look in the calling code:
SELECT INTO x * FROM mytable WHERE foo = bar;
PERFORM generic_function(x,'mytable');
Elegant-but-stupid solution
To allow the call-signature I show up-top, the best solution I could come up with is a high-overhead guess-and-check approach which would probably be an extremely bad idea to use in production. You can find the sqlfiddle here.
There is a simple solution. Use pg_typeof():
CREATE OR REPLACE FUNCTION generic_function(row_data ANYELEMENT)
RETURNS text AS
$func$
DECLARE
table_name text := pg_typeof($1);
BEGIN
RETURN table_name;
END
$func$ LANGUAGE plpgsql;
Obviously, this can only work for registered composite types. Every table or view falls into this category. But not derived tables.
Test:
CREATE TEMP TABLE foo (id int);
SELECT generic_function(NULL::foo);
Here is a search for related answers.
There isn't any solution on plpgsql level. Theoretically you can do it for some cases in C extension, but the code will be pretty complex. Postgres, PLpgSQL is designed as static, type rigid environment - and dynamic SQL doesn't change it too much (design prefer speed, less memory usage). Just don't design too dynamic code inside Postgres.

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

plpgsql - column type auto detect by returning a resultset

I got Postgresql 8.4
I have a user table
user_id INT,
user_name VARCHAR(255),
user_email VARCHAR(255),
user_salt VARCHAR(255)
and 2 functions:
one with SETOF:
CREATE FUNCTION test ()
RETURNS SETOF "user"
AS
$BODY$
BEGIN
RETURN QUERY SELECT
*
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
one with TABLE:
CREATE FUNCTION test ()
RETURNS TABLE (id INT, name VARCHAR, email VARCHAR)
AS
$BODY$
BEGIN
RETURN QUERY SELECT
"user".user_id, "user".user_name, "user".user_email
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
By SETOF the column type is filled automatically, but I cannot set the column name and which columns to select in the result. By TABLE I can cut off the user_ prefix and set the exact column names, but I have to set the column types manually.
Is it possible to got the advantages of both?
Type handling with in PostgreSQL (and SQL in general) is strict. Defining the RETURN type of a function can be tricky.
There are simple solutions with plain SQL:
Select and rename all columns:
SELECT * FROM t AS t(id, name, email, salt);
Select and rename some columns:
SELECT user_id AS id, user_name AS name FROM t;
Combine with function
If you need a server side function for some reason, you can still combine these SQL features with a function. Given a table t like defined in your question and this simple function:
CREATE OR REPLACE FUNCTION f_test()
RETURNS SETOF t AS
$func$
SELECT * FROM t -- do stuff here
$func$ LANGUAGE sql STABLE;
To only rename columns (no need to provide types):
SELECT * FROM f_test() t(id, name, email, salt)
Select and rename some columns:
SELECT user_id AS id, user_name AS name FROM f_test() t;
You could possibly combine this with various different functions on different tables:
Refactor a PL/pgSQL function to return the output of various SELECT queries
But I am not entirely sure which problem in particular you want to tackle here.
Asides
Never quote the language name. plpgsql is an identifier. Quoting can lead to unexpected problems.
A function that only selects from tables can be declared STABLE. May be beneficial with repeated calls when nested.
Generally I would advice to upgrade to a current version of Postgres. 8.4 is rather old and will be lose support in less than a year. There have been a number of improvements in this area since 8.4.
I don't think this is possible in pl/pgsql because, it strongly depends on user defined types. Sadly this language is not smart enough for type auto detection... I think my first possible solution I'll use, it solves the problem partially because at least I won't need to refactor every function manually by type change of a table column.
1.) Possible solution with asking column types:
CREATE FUNCTION test ()
RETURNS TABLE (id "user".user_id%TYPE, name "user".user_name%TYPE, email "user".user_email%TYPE)
AS
$BODY$
BEGIN
return QUERY SELECT
"user".user_id, "user".user_name, "user".user_email
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
With this at least the type is not redundant. Not the best, but acceptable.
2.) Possible solution with SETOF RECORD:
CREATE FUNCTION test ()
RETURNS SETOF RECORD
AS
$BODY$
BEGIN
RETURN QUERY SELECT
"user".user_id AS id, "user".user_name AS name, "user".user_email AS email
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
Without column type definition list I got the following error:
ERROR: a column definition list is required for functions returning
"record"
So I have to use it like this:
SELECT * FROM test() AS (id INT, name VARCHAR, email VARCHAR);
Instead of this:
SELECT * FROM test()
I got every column in string by the php client, so the column type definition is more than useless for me... This solution would be the best without column type definition, but with it not acceptable.
It is possible to use this similar to table:
CREATE FUNCTION test (OUT id "user".user_id%TYPE, OUT name "user".user_name%TYPE, OUT email "user".user_email%TYPE)
RETURNS SETOF RECORD
AS
$BODY$
BEGIN
RETURN QUERY SELECT
"user".user_id, "user".user_name, "user".user_email
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
For example I could set everything to TEXT:
CREATE FUNCTION test (OUT id TEXT, OUT name TEXT , OUT email TEXT )
RETURNS SETOF RECORD
AS
$BODY$
BEGIN
RETURN QUERY SELECT
"user".user_id::TEXT , "user".user_name::TEXT , "user".user_email::TEXT
FROM
"user";
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
This works, but this is far from type auto detection, and it would result a lot of redundant text converter code...
3.) Possible solution with refcursors:
CREATE FUNCTION test ()
RETURNS SETOF "user"
AS
$BODY$
DECLARE
refc "user";
BEGIN
FOR refc IN
SELECT
"user".user_id, "user".user_name, "user".user_email
FROM
"user"
LOOP
RETURN NEXT refc;
END LOOP ;
RETURN ;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
This fills out the lacking columns with null values, I cannot name the columns, and sql loops are very slow... So this is not acceptable.
By refcursors there is another way: to return the refcursor itself, but it is not acceptable because I cannot use it as a normal variable, I have to give a string as cursor name... Btw I did not manage to use the refcursor itself as result in phpstorm. I got jdbc cursor not found error. Maybe I set the name wrong, I don't know, I don't think it's worth more effort.