PostgreSQL error in creating function with varying columns using RECORD - postgresql

I have this function that should be returning a varying columns, can I get a help on what is wrong with my code?
CREATE OR REPLACE FUNCTION functions.search(column_name VARCHAR(40))
RETURNS SETOF RECORD AS $$
DECLARE
rec RECORD;
BEGIN
RETURN QUERY EXECUTE format('SELECT %I FROM students_table);
END;
$$ LANGUAGE plpgsql;
Call:
SELECT * FROM functions.search(
('Student Id', 'Subect')
) as ("Student Id" bigint, "Subect" text)
Here's my error
ERROR: function functions.search(record) does not exist
LINE 1: SELECT * FROM functions.search(
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 15
Thank you very much

There are several things wrong here.
First, to support a variable number of arguments, you need a VARIADIC parameters. Then, your format function call is somewhat truncated and syntactical nonsense.
You could experiment with a function like this:
CREATE FUNCTION functions.search(VARIADIC column_names text[]) RETURNS SETOF record
LANGUAGE plpgsql AS
$$BEGIN
RETURN QUERY EXECUTE
format('SELECT %s FROM students_table',
(SELECT string_agg(quote_ident(s), ', ')
FROM unnest(column_names) AS cols(s))
);
END;$$;
You can call it like this:
SELECT *
FROM functions.search('Student Id', 'Subect')
AS ("Student Id" bigint, "Subect" text);

Related

Using a parameter as return type in plpgsql function

Say I have this query:
CREATE OR REPLACE FUNCTION select_from_table(table_name varchar(63))
RETURNS SETOF table_name AS
$$
DECLARE
query TEXT := 'SELECT * FROM ' || table_name;
BEGIN
RETURN QUERY EXECUTE query;
END;
$$ LANGUAGE plpgsql;
Now if I try to execute it, I get error: type "table_name" does not exist and that's probably because I can only use the parameters within the function (between the dollar quotes) and not in the return type definition.
The question would be: are there any ways to SELECT from a table by passing its name as a parameter instead of hard coding it to the function? How?

Passing table names in an array

I need to do the same deletion or purge operation (based on several conditions) on a set of tables. For that I am trying to pass the table names in an array to a function. I am not sure if I am doing it right. Or is there a better way?
I am pasting just a sample example this is not the real function I have written but the basic is same as below:
CREATE OR REPLACE FUNCTION test (tablename text[]) RETURNS int AS
$func$
BEGIN
execute 'delete * from '||tablename;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
But when I call the function I get an error:
select test( {'rajeev1'} );
ERROR: syntax error at or near "{"
LINE 10: select test( {'rajeev1'} );
^
********** Error **********
ERROR: syntax error at or near "{"
SQL state: 42601
Character: 179
Array syntax
'{rajeev1, rajeev2}' or ARRAY['rajeev1', 'rajeev2']. Read the manual.
TRUNCATE
Since you are deleting all rows from the tables, consider TRUNCATE instead. Per documentation:
Tip: TRUNCATE is a PostgreSQL extension that provides a faster
mechanism to remove all rows from a table.
Be sure to study the details. If TRUNCATE works for you, the whole operation becomes very simple, since the command accepts multiple tables:
TRUNCATE rajeev1, rajeev2, rajeev3, ..
Dynamic DELETE
Else you need dynamic SQL like you already tried. The scary missing detail: you are completely open to SQL injection and catastrophic syntax errors. Use format() with %I (not %s to sanitize identifiers like table names. Or, better yet in this particular case, use an array of regclass as parameter instead:
CREATE OR REPLACE FUNCTION f_del_all(_tbls regclass)
RETURNS void AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOREACH _tbl IN ARRAY _tbls LOOP
EXECUTE format('DELETE * FROM %s', _tbl);
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT f_del_all('{rajeev1,rajeev2,rajeev3}');
Explanation here:
Table name as a PostgreSQL function parameter
You used wrong syntax for text array constant in the function call. But even if it was right, your function is not correct.
If your function has text array as argument you should loop over the array to execute query for each element.
CREATE OR REPLACE FUNCTION test (tablenames text[]) RETURNS int AS
$func$
DECLARE
tablename text;
BEGIN
FOREACH tablename IN ARRAY tablenames LOOP
EXECUTE FORMAT('delete * from %s', tablename);
END LOOP;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
You can then call the function for several tables at once, not only for one.
SELECT test( '{rajeev1, rajeev2}' );
If you do not need this feature, simply change the argument type to text.
CREATE OR REPLACE FUNCTION test (tablename text) RETURNS int AS
$func$
BEGIN
EXECUTE format('delete * from %s', tablename);
RETURN 1;
END
$func$ LANGUAGE plpgsql;
SELECT test('rajeev1');
I recommend using the format function.
If you want to execute a function (say purge_this_one_table(tablename)) on a group of tables identified by similar names you can use this construction:
create or replace function purge_all_these_tables(mask text)
returns void language plpgsql
as $$
declare
tabname text;
begin
for tabname in
select relname
from pg_class
where relkind = 'r' and relname like mask
loop
execute format(
'purge_this_one_table(%s)',
tabname);
end loop;
end $$;
select purge_all_these_tables('agg_weekly_%');
It should be:
select test('{rajeev1}');

It could refer to either a PL/pgSQL variable or a table column

I have a function in pgsql
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(date_in_ad date)
RETURNS character varying AS
$$
BEGIN
RETURN(
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = $1
);
END
$$
LANGUAGE plpgsql;
It is created with no errors, but when i use this function it through following error:
ERROR: column reference "date_in_ad" is ambiguous
LINE 3: WHERE date_in_ad = $1
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
QUERY: SELECT (
SELECT MAX(date_in_bs) FROM core.date_conversion
WHERE date_in_ad = $1
)
CONTEXT: PL/pgSQL function core.date_bs_from_ad(date) line 3 at RETURN
********** Error **********
ERROR: column reference "date_in_ad" is ambiguous
SQL state: 42702
Detail: It could refer to either a PL/pgSQL variable or a table column.
Context: PL/pgSQL function core.date_bs_from_ad(date) line 3 at RETURN
In cases like these, where the code is simple straightforward enough, sometimes it is useful to rely on one of these special plpgsql commands at the start of the function text:
#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column
In this case, it would be used as follows:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(date_in_ad date)
RETURNS character varying AS
$$
#variable_conflict use_column
BEGIN
RETURN(
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = $1
);
END
$$
This is especially useful for cases when the clash is not with the parameters, but rather with the output column names, such as this:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(p_date_in_ad date)
RETURNS TABLE (date_in_bs character varying) AS
$$
BEGIN
RETURN QUERY
SELECT date_in_bs FROM core.date_conversion
WHERE date_in_ad = p_date_in_ad;
END;
$$
The function above will fail because it the compiler cannot decide if date_in_bs is the output variable name or one of core.date_conversion's columns. For problems like these, the command #variable_conflict use_column can really help.
There is a collision between SQL identifier and PlpgSQL variable. There are no clean, what do you want. You wrote a predicate, that is TRUE always.
Good to use:
prefix (usually "_") for local variables
qualified names in embedded SQL - like table_name.column_name
so both techniques (only one is necessary)
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(_date_in_ad date)
RETURNS character varying AS $$
BEGIN
RETURN SELECT dc.date_in_bs
FROM core.date_conversion dc
WHERE dc.date_in_ad = _date_in_ad;
END
$$ LANGUAGE plpgsql;
For these one line functions is SQL language better:
CREATE OR REPLACE FUNCTION core.date_bs_from_ad(_date_in_ad date)
RETURNS character varying AS $$
SELECT dc.date_in_bs
FROM core.date_conversion dc
WHERE dc.date_in_ad = $1;
$$ LANGUAGE sql;

How to return multiple rows from PL/pgSQL function?

I have spent good amount of time trying to figure it out and I haven't been able to resolve it. So, I need your help please.
I am trying to write a PL/pgSQL function that returns multiple rows. The function I wrote is shown below. But it is not working.
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY SELECT department_id into result_record.visits
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields;
It is returning this error:
ERROR: RETURN cannot have a parameter in function returning set;
use RETURN NEXT at or near "QUERY"
After fixing the bugs #Pavel pointed out, also define your return type properly, or you have to provide a column definition list with every call.
This call:
SELECT * FROM get_object_fields()
... assumes that Postgres knows how to expand *. Since you are returning anonymous records, you get an exception:
ERROR: a column definition list is required for functions returning "record"
One way (of several) to fix this is with RETURNS TABLE (Postgres 8.4+):
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS TABLE (department_id int) AS
$func$
BEGIN
RETURN QUERY
SELECT department_id
FROM fact_department_daily
WHERE report_date = '2013-06-07';
END
$func$ LANGUAGE plpgsql;
Works for SQL functions just the same.
Related:
PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"
I see more bugs:
first, a SET RETURNING FUNCTIONS call has following syntax
SELECT * FROM get_object_fields()
second - RETURN QUERY forwards query result to output directly. You cannot store this result to variable - it is not possible ever in PostgreSQL now.
BEGIN
RETURN QUERY SELECT ....; -- result is forwarded to output directly
RETURN; -- there will not be any next result, finish execution
END;
third - these simple functions is better to implement in SQL languages
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS SETOF RECORD AS $$
SELECT department_id WHERE ...
$$ LANGUAGE sql STABLE;
Here's one way
drop function if exists get_test_type();
drop type if exists test_comp;
drop type if exists test_type;
drop type if exists test_person;
create type test_type as (
foo int,
bar int
);
create type test_person as (
first_name text,
last_name text
);
create type test_comp as
(
prop_a test_type[],
prop_b test_person[]
);
create or replace function get_test_type()
returns test_comp
as $$
declare
a test_type[];
b test_person[];
x test_comp;
begin
a := array(
select row (m.message_id, m.message_id)
from message m
);
-- alternative 'strongly typed'
b := array[
row('Bob', 'Jones')::test_person,
row('Mike', 'Reid')::test_person
]::test_person[];
-- alternative 'loosely typed'
b := array[
row('Bob', 'Jones'),
row('Mike', 'Reid')
];
-- using a select
b := array (
select row ('Jake', 'Scott')
union all
select row ('Suraksha', 'Setty')
);
x := row(a, b);
return x;
end;
$$
language 'plpgsql' stable;
select * from get_test_type();
CREATE OR REPLACE FUNCTION get_object_fields()
RETURNS table (department_id integer)
AS
$$
DECLARE result_record keyMetrics;
BEGIN
return QUERY
SELECT department_id
from fact_department_daily
where report_date='2013-06-07';
--return result_record;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_object_fields()

PostgreSQL 9.3: Get list of table names present in view using function

I want to get list of table names present in the view.
So I have created the function with one parameter(view_name) to get table names.
Function : funtion_GetTables_FromView
CREATE OR REPLACE FUNCTION funtion_GetTables_FromView
(
view_Name varchar
)
RETURNS TABLE
(
TableNames varchar
) AS
$BODY$
DECLARE
v_SQL varchar;
BEGIN
v_SQL := 'SELECT Table_Name
FROM INFORMATION_SCHEMA.VIEW_TABLE_USAGE
WHERE View_Name = ''' || view_Name || '''';
RAISE INFO '%',v_SQL;
RETURN QUERY EXECUTE v_SQL;
END;
$BODY$
LANGUAGE PLPGSQL;
Calling function:
select * from funtion_GetTables_FromView('myview');
But getting an error:
ERROR: structure of query does not match function result type
You only showed a part of the error message, the complete error message gives the actual reason:
ERROR: structure of query does not match function result type
Detail: Returned type information_schema.sql_identifier does not match expected type character varying in column 1.
Where: PL/pgSQL function funtion_gettables_fromview(character varying) line 14 at RETURN QUERY
So the column information_schema.table_name is not a varchar column. The immediate fix for this is to cast the column to the required type:
v_SQL := 'SELECT Table_Name::varchar
FROM INFORMATION_SCHEMA.VIEW_TABLE_USAGE
WHERE View_Name = ''' || view_Name || '''';
But the whole function is needlessly complex and error prone. A simply SQL function will do just fine:
CREATE OR REPLACE FUNCTION funtion_GetTables_FromView(v_viewname varchar)
RETURNS TABLE(tablenames varchar)
AS
$$
SELECT table_name
FROM information_schema.view_table_usage
WHERE view_Name = v_viewname;
$$
LANGUAGE sql;
For some reason this does not require the cast to varchar. I suspect this has something to do with running dynamic SQL inside PL/pgSQL.
Unrelated, but: I personally find it pretty useless to prefix a function with function_. It is obvious when using it, that it is a function. Are you prefixing all your classes with class_ and all your methods with method_ in your programming language?