Recursive using SetOf function in the FROM clause - postgresql

I need the foo(1) UNION foo(2) UNION foo(...) in a WITH RECURSIVE, is possible?
This is the function and what I try to be recursive:
CREATE FUNCTION foo(bit_len int) RETURNS setof bigint as $f$
SELECT i::bigint | (2^bit_len)::bigint as x
FROM generate_series(0,(2^bit_len)::bigint-1) s(i);
$f$ LANGUAGE SQL IMMUTABLE;
WITH RECURSIVE gx(n) AS ( -- need from foo(1) to foo(5)
SELECT t1.x FROM foo(1) t1(x)
UNION ALL
SELECT t2.x FROM foo(n+1) t2(x) WHERE n<=5
)
SELECT * FROM gx;
ERROR column "n" does not exist at foo(n+1).

Related

How to return any* type from postgres function?

I can returns setof record and provide definition list to get result:
select (f.row).* from tree( 'resource_type' ) f( id int, path int[], fullname text, level int, row resource_type );
But how to rewrite my function to return any type with automatic cast?
This does not work:
create or replace function tree( _tbl regclass )
--returns setof record
returns table( id int, path int[], fullname text, level int, r record )
as $$
BEGIN
return query execute format( '
WITH RECURSIVE parent ( id, path, fullname, level, row ) AS (
SELECT
id,
ARRAY[ id ],
''/'' || name::text,
1,
%1$s
FROM %1$s
WHERE parent_id = 13
UNION ALL
SELECT
child.id,
parent.path || child.id,
parent.fullname || ''/'' || child.name,
parent.level +1,
child
FROM parent
JOIN %1$s child ON parent.id = child.parent_id
WHERE NOT child.id = ANY(path)
)
SELECT * FROM parent ', _tbl);
END;
$$ LANGUAGE plpgsql
=> select f.r from tree( 'resource_type' ) f
ERROR: structure of query does not match function result type
DETAIL: Returned type resource_type does not match expected type record in column 5.
CONTEXT: PL/pgSQL function tree(regclass) line 3 at RETURN QUERY
The way to write a polymorphic function is to use anyelement. But you must provide an argument of type anyelement as well. The actual data type will then be deduced from the type of that argument:
CREATE FUNCTION poly(
outtype anyelement,
otherparam integer
) RETURNS TABLE (res anyelement)
LANGUAGE plpgsql AS
$$DECLARE
resulttype regtype := pg_typeof(outtype);
whetever text;
BEGIN
/* calculate a text representation of the result in "whatever" */
EXECUTE format('SELECT CAST ($1 AS %s)', resulttype)
USING whatever INTO res;
RETURN NEXT;
END;$$
You call a function like this by supplying a dummy argument of the desired type:
SELECT * FROM poly(NULL::myresulttype, 42);
The solution is to pass anyelement as argument, then I can use anyelement for returns ... statement. doc
create or replace function tree( _tbl anyelement )
returns table( id int, path int[], fullname text, level int, "row" anyelement )
as $$
BEGIN
return query execute format( '...', pg_typeof( _tbl )::text );
END;
$$ LANGUAGE plpgsql

How convert recursive to function in plpgsql?

i have that working code, but i need to convert it to function with dynamic attribute tid=1645 where number 1645 will always change.
with recursive r as (
select tid, boss from titles where tid=1645
union
select titles.tid, titles.boss from titles join r on titles.tid = r.boss
)
select * from r
Now, i have the same:
DROP FUNCTION bosses_of_rsd_tids(integer);
CREATE OR REPLACE FUNCTION public.bosses_of_rsd_tids(rsd_tid int)
RETURNS table (c_tid int, c_boss int)
LANGUAGE plpgsql
AS $function$
begin
with recursive r as (
select tid, boss from titles where tid=rsd_tid
union
select titles.tid, titles.boss from titles join r on titles.boss = r.tid
)
select c_tid, c_boss;
end;
$function$
;
As a result i need table of results... I tried to return select c_tid, c_boss; but have error: error near return
CREATE OR REPLACE FUNCTION public.bosses_of_rsd_tids(rsd_tid int)
RETURNS TABLE (c_tid int, c_boss int) AS
$func$
BEGIN
RETURN QUERY
WITH RECURSIVE r AS (
SELECT tid, boss
FROM titles
WHERE tid = rsd_tid
UNION ALL -- ?!
SELECT t.tid, t.boss
FROM r
JOIN titles t ON t.tid = r.boss -- !
)
TABLE r; -- !
END
$func$ LANGUAGE plpgsql;
You want UNION ALL instead of UNION as it doesn't make sense to try and fold duplicates climbing the hierarchy. (A duplicate would initiate an endless loop.)
TABLE r is short for SELECT * FROM r. Your org. select c_tid, c_boss was wrong. See:
Is there a shortcut for SELECT * FROM?
Can also be a simpler SQL function:
CREATE OR REPLACE FUNCTION public.bosses_of_rsd_tids(rsd_tid int)
RETURNS TABLE (c_tid int, c_boss int) AS
$func$
WITH RECURSIVE r AS (
SELECT tid, boss
FROM titles
WHERE tid = rsd_tid
UNION ALL
SELECT t.tid, t.boss
FROM r
JOIN titles t ON t.tid = r.boss
)
TABLE r;
$func$ LANGUAGE sql;
See:
Difference between language sql and language plpgsql in PostgreSQL functions
You have to use "return query" on all the query (with included)
You forgot the "from r" in the main select
/* EDIT */
in you example you select c_tid and c_boss instead of tid and boss
and the test of the join is inverted
request updated:
DROP FUNCTION bosses_of_rsd_tids(integer);
CREATE OR REPLACE FUNCTION public.bosses_of_rsd_tids(rsd_tid int)
RETURNS table (c_tid int, c_boss int)
LANGUAGE plpgsql
AS $function$
begin
return query with recursive r as (
select tid, boss from titles where tid=rsd_tid
union
select titles.tid, titles.boss from titles join r on titles.tid = r.boss )
select tid, boss from r;
end;
$function$
;

Greenplum/Postgres 8 function dynamic result set?

I need to write a function that returns a table with unknown number of columns.
If i receive 'None' in column input parameter then that column shouldn't be included in the output. In postgres 9+ there is a solution for this problem.
something like below:
CREATE OR REPLACE FUNCTION data_of(id integer,col1 varchar,col2 varchar, col3 varchar)
RETURNS TABLE (count_rec, dimensions text[] ) AS
$func$
DECLARE
_dimensions text := 'col1, col2, col3'; -- If i receive 'None' in input param then i exclude that from column list
BEGIN
RETURN QUERY EXECUTE format('
SELECT count(*) as count_rec,
string_to_array($1) -- AS dimensions
FROM x
WHERE id = $2'
, _dimensions)
USING _dimensions , _id;
END
$func$ LANGUAGE plpgsql;
But in Greenplum (Postgres 8.2) i could not find any. Is there any similar solution?
thanks
You have 2 options to do it: use set-returning function returning "record" or returning your custom type.
First option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create or replace function test_func(column_list varchar[]) returns setof record as $BODY$
declare
r record;
begin
for r in execute 'select ' || array_to_string(column_list, ',') || ' from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func(array['a','c','e']) as f(a int, c int, e varchar);
Second option:
create table test (a int, b int, c int, d varchar, e varchar, f varchar);
insert into test select id, id*2, id*3, (id*4)::varchar, (id*4)::varchar, (id*4)::varchar from generate_series(1,10) id;
create type testtype as (
a int,
c int,
e varchar
);
create or replace function test_func() returns setof testtype as $BODY$
declare
r testtype;
begin
for r in execute 'select a,c,e from test' loop
return next r;
end loop;
return;
end;
$BODY$
language plpgsql
volatile;
select * from test_func();
But I'm 99% sure you're trying to do something wrong. In Greenplum the result of function execution cannot be used as a "table" in join conditions, because the function executes on the master. You even won't be able to create a table out of the last query returning the data from your function because of this limitation
In short, this is not a recommended way to work with data in Greenplum

PostgreSQL 9.3: missing FROM-clause entry for table

I have a table with two columns.
Example:
create table t1
(
cola varchar,
colb varchar
);
Now I want to insert the rows from function.
In the function: I want to use two parameters which is of type varchar to insert the values into the above table. I am passing the string to insert into the table.
I am passing two string of characters as a parameters to the function:
Parameters:
cola varchar = 'a,b,c,d';
colb varchar = 'e,f,g,h';
The above parameters have to insert into the table like this:
cola colb
----------------
a e
b f
c g
d h
My try:
create or replace function fun_t1(cola varchar,colb varchar)
returns void as
$body$
Declare
v_Count integer;
v_i integer = 0;
v_f1 text;
v_cola varchar;
v_colb varchar;
v_query varchar;
Begin
drop table if exists temp_table;
create temp table temp_table
(
cola varchar,
colb varchar
);
v_Count := length(cola) - length(replace(cola, ',', ''));
raise info '%',v_Count;
WHILE(v_i<=v_Count) LOOP
INSERT INTO temp_table
SELECT LEFT(cola,CHARINDEX(',',cola||',',0)-1)
,LEFT(colb,CHARINDEX(',',colb||',',0)-1);
cola := overlay(cola placing '' from 1 for CHARINDEX(',',cola,0));
colb := overlay(colb placing '' from 1 for CHARINDEX(',',colb,0));
v_i := v_i + 1;
END LOOP;
for v_f1 IN select * from temp_table loop
v_cola := v_f1.cola; /* Error occurred here */
v_colb := v_f1.colb;
v_query := 'INSERT INTO t1 values('''||v_cola||''','''||v_colb||''')';
execute v_query;
end loop;
end;
$body$
language plpgsql;
Note: In the function I have used temp_table that is according to the requirement which
I am using for the other use also in the function which I have not display here.
Calling function:
SELECT fun_t1('a,b,c','d,e,f');
Getting an error:
missing FROM-clause entry for table "v_f1"
Try this way using split_part() : -
create or replace function ins_t1(vala varchar,valb varchar,row_cnt int) returns void as
$$
BEGIN
FOR i IN 1..row_cnt LOOP -- row_cnt is the number rows you need to insert (ex. 4 or 5 or whatever it is)
insert into t1 (cola,colb)
values (
(select split_part(vala,',',i))
,(select split_part(valb,',',i))
);
END LOOP;
END;
$$
language plpgsql
function call :select ins_t1('a,b,c,d','e,f,g,h',4)
As mike-sherrill-cat-recall said in his answer by using regexp_split_to_table
create or replace function fn_t1(vala varchar,valb varchar) returns void
as
$$
insert into t1 (cola, colb)
select col1, col2 from (select
trim(regexp_split_to_table(vala, ',')) col1,
trim(regexp_split_to_table(valb, ',')) col2)t;
$$
language sql
function call :select fn_t1('a,b,c,d','e,f,g,h')
If there's no compelling reason to use a function for this, you can just split the text using a regular expression. Here I've expressed your arguments as a common table expression, but that's just for convenience.
with data (col1, col2) as (
select 'a, b, c, d'::text, 'e, f, g, h'::text
)
select
trim(regexp_split_to_table(col1, ',')) as col_a,
trim(regexp_split_to_table(col2, ',')) as col_b
from data;
col_a col_b
--
a e
b f
c g
d h
If there is a compelling reason to use a function, just wrap a function definition around that SELECT statement.
create function strings_to_table(varchar, varchar)
returns table (col_a varchar, col_b varchar)
as
'select trim(regexp_split_to_table($1, '','')),
trim(regexp_split_to_table($2, '',''));'
language sql
stable
returns null on null input;
select * from strings_to_table('a,b,c,d', 'e,f, g, h');
col_a col_b
--
a e
b f
c g
d h
My personal preference is usually to build functions like this to return tables rather than inserting into tables. To insert, I'd usually write a SQL statement like this.
insert into foo (col_a, col_b)
select col_a, col_b from strings_to_table('a,b,c,d', 'e,f,g,h');
The simpest way is using plpython for this.
create or replace function fill_t1(cola varchar, colb varchar) returns void as $$
for r in zip(cola.split(','), colb.split(',')):
plpy.execute(plpy.prepare('insert into t1(cola, colb) values ($1, $2)', ['varchar', 'varchar']), [r[0], r[1]])
$$ language plpythonu;
The result:
# create table t1 (cola varchar, colb varchar);
CREATE TABLE
# select fill_t1('1,2,3', '4,5,6');
fill_t1
---------
(1 row)
# select * from t1;
cola | colb
------+------
1 | 4
2 | 5
3 | 6
(3 rows)
You can read about Python zip function here: https://docs.python.org/2/library/functions.html#zip

How to specify column types for CTE (Common Table Expressions) in PostgreSQL?

Consider
WITH t (f0, f1) as (
values
(1, 10),
(2, 20)
)...
How do I specify that f0 and f1 are of type bigint?
I think you'd have to specify the types inside the VALUES expression in your case:
WITH t (f0, f1) as (
values
(1::bigint, 10::bigint),
(2, 20)
)...
You only need the types on the first set of values, PostgreSQL can infer the rest.
For example, suppose we have two functions:
create function f(bigint, bigint) returns bigint as $$
begin
raise notice 'bigint';
return $1 * $2;
end;
$$ language plpgsql;
create function f(int, int) returns int as $$
begin
raise notice 'int';
return $1 * $2;
end;
$$ language plpgsql;
Then
WITH t (f0, f1) as (
values
(1, 10),
(2, 20)
)
select f(f0, f1) from t;
will give you two int notices whereas
WITH t (f0, f1) as (
values
(1::bigint, 10::bigint),
(2, 20)
)
select f(f0, f1) from t;
would give you two bigint notices.
I don't know if this will work for you, but casting the column you want to specify worked for me in DB2
WITH CTE AS (
SELECT
'Apple' AS STRING1
,CAST('Orange' AS VARCHAR(10000)) AS STRING2
FROM SYSIBM . SYSDUMMY1)
SELECT * FROM CTE