Simple PostgreSQL function to return rows - postgresql

How do I convert a simple select query like select * from customers into a stored procedure / function in pg?
I'm new to Postgres and create function customers() as returns table/setof just didn't feel right and thus the question here.
I understand procs are called "functions" in pg land. Thus create procedure does not exist and my only options are to either create a view or a function. The issue is create function x() returns setof y returns a paren'd comma separated row of values which can't be used without further processing (at least that's what I'm seeing in pgAdmin and Ruby/Sequel).
create function x() returns table(...) requires I embed the row definition which I don't want to.
I'm sure there's a reason behind all this but I'm surprised that the most common use case is this tricky.

Untested but should be about right:
CREATE OR REPLACE FUNCTION getcustomers() RETURNS SETOF customers AS $$
SELECT * FROM customers;
$$ LANGUAGE sql;

The issue is "create function x() returns setof y" returns a paren'd
comma separated row values which can't be used without further processing
The function returns a row. To decompose into individual columns, call it with:
SELECT * FROM getcustomers();
That's assuming the function defines a proper return type. See:
How to return multiple rows from PL/pgSQL function?
The manual on CREATE FUNCTION should be a good starting point. The example section covers this topic.

Related

How to pass a Tables Column into a plpgsql Functiion while performing a SELECT... statement

I googled but everyone was asking for how to pass tables or how to use the return result into a Function; I want to do neither. I simply want to take the value of a Column (lets assume col2 below is of the text datatype) of a table, and pass that data into a Function, so I can manipulate the data, but in the SELECT... statement itself, i.e.
SELECT t.col1, "myCustomFunction"(t.col2)
FROM tbl t
WHERE t.col1 = 'someCondition';
CREATE OR REPLACE FUNCTION myCustomFunction(myArg text)
RETURNS text AS $$
DECLARE
BEGIN
RETURN UPPER(myArg);
END
$$ LANGUAGE plpgsql;
... So if myCustomerFunction()'s job was capitalize letters (its not, just an example), the output would be the table with col2 data all capitalized.
Is this possible? I supposed it would be no different than embedding a CASE expression there, which I know works, and a Function returns a result, so I assumed it would be the same, but I am getting SQL Error
You cannot to pass named column to some function and you cannot to return this named column like table with this column. The table is set of rows, and almost all processing in Postgres is based on rows processing. Usually you need to hold only data of one row in memory, so you can process much bigger dataset than is your memory.
Inside PL/pgSQL function you have not informations about outer. You can get just data of scalar types, arrays of scalars, or composite or arrays of composites (or ranges and multiranges - this special kind of composite and array of composite). Nothing else.
Theoretically you can aggregate data in one column to array, and later you can expand this array to table. But these operations are memory expensive and can be slow. You need it only in few cases (like computing of median function), but it is slow, and there is risk of out of memory exception.
When object names are not doubled quoted Postgres processes then internally as lower case. When doubled quoted the are processed exactly as quoted. The thing is these may not be the same. You defined the function as FUNCTION myCustomFunction(myArg text) Not doubled quoted, but attempt to call it via "myCustomFunction"(t.col2). Unfortunately myCustomFunction processed as mycustomfunction but "myCustomFunction" is processed exactly as it appears. Those are NOT the same. Either change your select to:
SELECT t.col1,myCustomFunction(t.col2)
FROM tbl t
WHERE t.col1 = 'someCondition';
or change the function definition to:
CREATE OR REPLACE FUNCTION "myCustomFunction"(myArg text)
RETURNS text AS $$
DECLARE
BEGIN
RETURN UPPER(myArg);
END
$$ LANGUAGE plpgsql;

Create view using postgres function

I'm trying to build a parametrized view using a postgres function:
CREATE FUNCTION schemaB.testFunc(p INT)
RETURNS TABLE
AS
RETURN (SELECT * FROM schemaZ.mainTable WHERE id=p)
The problem is always the same:
SQL Error [42601]: ERROR: syntax error at or near "AS"
Any idea on what could I be doing wrong?
You need to specify the columns of the "return table", this is either done using
returns table(col_1 integer, col_2 text, ...)
In your case you are returning only rows of one table, so it's easier to use
returns setof maintable
As documented in the manual the function body needs to be enclosed in single quotes, or using dollar quoting.
As stored functions can be written in many different languages in Postgres, you also need to specify a language - in this case language sql is suitable.
So putting all that together, you need:
CREATE FUNCTION schemaB.testFunc(p_id INT)
RETURNS setof schemaZ.mainTable
AS
$$
SELECT *
FROM schemaZ.mainTable
WHERE id = p_id
$$
language sql;
A return statement is not required for language sql functions.

Dynamic SQL and Setof Functions in PG

I am researching if it is possible to dynamically create a view and then return all data from it, all in a single function. My goal is to create a single function that will return all data from a dynamically created view, upon execution of the function. I can do it in Oracle and SQL Server, but so far I am not sure if it is that simple in Postgres. If someone could show code samples, ideas or point me in the right direction, I would be super happy :)
I already know how to create a SETOF function or a dynamic SQL function, but getting both to work in a single program/function function is a challenge that I cannot overcome so far ...
The returned content can be dynamic, the structure should be static.
CREATE OR REPLACE FUNCTION fx(OUT a int, OUT b int)
RETURNS SETOF record AS $$
BEGIN
RETURN QUERY EXECUTE format('SELECT i, i+1 FROM generate_series(1,3)');
END;
PostgreSQL documentation is good, please, don't be afraid to read it. The plpgsql related pages need no more than two hours.

Loop through table and return a specific column

Here is my function:
CREATE OR REPLACE FUNCTION getAllFoo()
RETURNS SETOF "TraderMonthOutstanding" AS
$BODY$
DECLARE
r record;
BEGIN
FOR r IN (select * from "TraderMonthOutstanding"
where "TraderId"=1 and "IsPaid"=false)
LOOP
RETURN NEXT r.TraderId;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE 'plpgsql' ;
I want only TraderId from this loop but I am not able to get it because it gives me an error.
What am I doing wrong?
You declared that getAllFoo() will RETURNS SETOF "TraderMonthOutstanding" (a set of rows with structure of table TraderMonthOutstanding) but then you RETURN NEXT r.TraderId (single field).
You need to either change the declared return type to RETURNS SETOF "TraderMonthOutstanding".TraderId%TYPE or RETURN NEXT r.
You should be returning a setof integer, or a setof TraderMonthOutstanding".TraderId%TYPE.
Or changing perhaps a setof record and returning next r, or event setof TraderMonthOutstanding and declaring r as a TraderMonthOutstanding row type instead of an anonymous record.
Better yet, this seems like the kind of case where you ought to create a view to avoid the overhead of the using a function altogether:
create view getAllFoo as
select *
from "TraderMonthOutstanding"
where "TraderId"=1 and "IsPaid"=false;
select * from getAllFoo;
It's usually terrible practice to loop over a set like that in a function if you're not doing anything useful. (Though I imagine the real one is doing something.)
Lastly, in the event you truly want this in a function, note that you can return query to avoid the loop altogether:
return query
select *
from "TraderMonthOutstanding"
where "TraderId"=1 and "IsPaid"=false;
What #Igor and #Denis said.
If you actually need the loop, which doesn't become apparent from your example, it would work like this:
CREATE OR REPLACE FUNCTION get_all_foo()
RETURNS SETOF "TraderMonthOutstanding"."TraderId"%TYPE AS
$func$
DECLARE
r "TraderMonthOutstanding"; -- use the well known type
BEGIN
FOR r IN
SELECT * FROM "TraderMonthOutstanding"
WHERE "TraderId" = 1
AND "IsPaid" = FALSE -- no parentheses needed
LOOP
-- do whatever necessitates a loop
RETURN NEXT r."TraderId";
END LOOP;
RETURN;
END
$func$ LANGUAGE plpgsql;
Do not quote the language name plpgsql. It's an identifier, not a literal. Current Postgres tolerates it, but it may be weeded out in future versions.
I would advice not to use CaMeL-case identifiers at all in Postgres. Even less if you double-quote some, but not others. That's a LoaDed Footgun. Read the manual about identifiers.
Since you already know the type of the record you are going to use, don't downgrade to an anonymous record, use the well known type to begin with: "TraderMonthOutstanding".
Like you have been told already, the type returned by RETURN has to match what you declared with RETURNS. Since you want only TraderId, adapt RETURNS accordingly. If you want to derive it from the existing table you can use the syntax displayed. Details in the manual. Or you can insert the actual type. Like integer or whatever.
And since you seem to have been using double-quoted CaMeL-case names when creating your table, you have to keep using them that way: double-quoted - even for fields of the derived type in the function body:
RETURN NEXT r."TraderId";
See what I mean with "LoaDed Footgun"?

In postgres (plpgsql), how to make a function that returns select * on a variable table_name?

Basically, at least for proof of concept, I want a function where I can run:
SELECT res('table_name'); and this will give me the results of SELECT * FROM table_name;.
The issue I am having is schema...in the declaration of the function I have:
CREATE OR REPLACE FUNCTION res(table_name TEXT) RETURNS SETOF THISISTHEPROBLEM AS
The problem is that I do not know how to declare my return, as it wants me to specify a table or a schema, and I won't have that until the function is actually run.
Any ideas?
You can do this, but as mentioned before you have to add a column definiton list in the SELECT query.
CREATE OR REPLACE FUNCTION res(table_name TEXT) RETURNS SETOF record AS $$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM ' || table_name;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM res('sometable') sometable (col1 INTEGER, col2 INTEGER, col3 SMALLINT, col4 TEXT);
Why for any real practical purpose would you just want to pass in table and select * from it? For fun maybe?
You can't do it without defining some kind of known output like jack and rudi show. Or doing it like depesz does here using output parameters http://www.depesz.com/index.php/2008/05/03/waiting-for-84-return-query-execute-and-cursor_tuple_fraction/.
A few hack around the wall approachs are to issue raise notices in a loop and print out a result set one row at a time. Or you could create a function called get_rows_TABLENAME that has a definition for every table you want to return. Just use code to generate the procedures creations. But again not sure how much value doing a select * from a table, especially with no constraints is other than for fun or making the DBA's blood boil.
Now in SQL Server you can have a stored procedure return a dynamic result set. This is both a blessing and curse as you can't be certain what comes back without looking up the definition. For me I look at PostgreSQL's implementation to be the more sound way to go about it.
Even if you manage to do this (see rudi-moore's answer for a way if you have 8.4 or above), You will have to expand the type explicitly in the select - eg:
SELECT res('table_name') as foo(id int,...)