Using variables from parent in nested postgresql function? - sql

I'm have a pretty long SQL routine that gets a few parameters and runs through a whole bunch of CASE statements. I have the following structure:
DO $$
DECLARE var INTEGER = "someColumn" FROM TABLE LIMIT 1;
BEGIN;
CREATE OR REPLACE FUNCTION pg_temp.fn(var2 boolean) returns decimal AS
$fn$ SELECT CASE WHEN var2 THEN var::decimal ELSE 0::decimal END $fn$ language sql;
$$
And using var inside of fn does not seem to quite work. As in the column does not exist. Is there a way to make it work, am I maybe thinking too complicated?
Edit: fixed the type that was missing. In the original code there is a type declaration, the declaration is not the problem. Using the variable in the nested function is.

First of all. THIS IS NOT THE BEST PRACTICE. It can be done, but it is not recommended to use nested functions.
The problem is that var is in a different scope than the function. You have to "write" the function as an independent unit. You can do this with EXECUTE and FORMAT.
Here is an example of a function that shows var as a message:
DO $$
DECLARE
sql text; -- To "write" the function
var integer := id FROM table_name ORDER BY id LIMIT 1;
BEGIN
-- To make it hapend I use $test$ to set the function.
-- FORMAT is to allocate var into the function through '%s'.
-- you have to use the correct sintax acording to the data type.
sql := FORMAT($test$
CREATE OR REPLACE FUNCTION test() RETURNS void AS $FUNCTION$
BEGIN
RAISE NOTICE '%s';
END; $FUNCTION$ LANGUAGE plpgsql
$test$,var);
EXECUTE sql;
END; $$

Related

Error while creating a User Defined Function in postgreSQL using SQL language

I am trying to create the following SQL(the only language I need for my use case) User Defined Function in PostgreSQL.
CREATE function sql_replace(textinput text,pattern character,lefttext integer,righttext integer)
returns text AS $$
DECLARE strlength integer default 0;
BEGIN
strlength = length($1);
return substr($1,1,$3 )
||regexp_replace(substr($1,$3+1,(strlength-$3-$4)),'[0-9]',$2)
||substr($1,(strlength-$4+1),$4 );
-- Additional logic
END;
$$ language SQL VOLATILE;
I have been encountering an error near line 3 in the declare statement. I have tried to push the declare statement inside the begin block but it didn't help. I have also tried to align the input type with SQL but got the same error.

plpgsql, variable inside query in function

I am new with plpgsql. I create new function
CREATE OR REPLACE FUNCTION createObj(number integer)
RETURNS INTEGER AS
$$
BEGIN
END;
$$
I have problem that if I want to make query in the body of the function and use in the query the the number variable while in the table their is a number the boolean is always true.
something like:
Select * from objects O, where O.number=number...
so number is not the number of the function but the filed in the table.
Their is a way to implement this and dont change the variable name?
Define your parameters with a prefix to distinguish them from columns:
CREATE OR REPLACE FUNCTION createObj(in_number integer)
RETURNS INTEGER AS
$$
BEGIN
Select * from
objects O
where O.number = in_number...
END;
$$

Return set of records with unknown table_name

I want to return a row from a table with this function (I don't know the name of the table, it's random)
CREATE FUNCTION foo( text ) RETURNS setof record AS $$
DECLARE
table_name ALIAS FOR $1;
BEGIN
SELECT * from table_name ;
END
$$ LANGUAGE plpgsql;
and then I want to do this:
select col1,col2 from foo('bar');
Any ideas?
SQL demands to know the return type at call time. And functions require you to define a return type as well. What you are after is not trivial.
If you don't know the return type at call time, you are basically out of luck. You cannot solve the problem with a single function call.
If you know the type at call time, there is an option with polymorphic types and dynamic SQL with EXECUTE:
CREATE OR REPLACE FUNCTION f_data_of_table(_tbl_type anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT * FROM ' || pg_typeof(_tbl_type);
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM f_data_of_table(NULL::my_table_name);
Details in this related answer:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Be wary of SQL injection:
Table name as a PostgreSQL function parameter
Only makes sense if you do more than just SELECT * FROM tbl, or you'd simply use the SQL command.
Aside:
Do not use ALIAS to attach names to parameter values. That's outdated and discouraged. Use named parameters instead.

"stable or volatile function is used as if it is immutable" warning in Postgresql function

I have a Postgresql function in which there is only a SELECT statement:
CREATE OR REPLACE FUNCTION fun_test()
RETURNS INTEGER AS $$
DECLARE size INTEGER;
BEGIN
SELECT COUNT(*) INTO size FROM tab;
RETURN size;
END;
$$ LANGUAGE plpgsql STABLE;
When I call the function with:
SELECT fun_test()
Although the result are correct, there will be a warning too:
WARNING: A stable or volatile function is used as if it is immutable
HINT: The function should be declared as stable or volatile in create function statement.
I found in Postgresql document that STABLE is a appropriate selection for functions whose results depend on database lookups, parameter variables (such as the current time zone), etc. http://www.postgresql.org/docs/8.2/static/sql-createfunction.html
My question is where the warning comes from? It seems that I am doing what the document requires to do. Any help is appreciated.
EDIT:
I am using postgresql server 8.2.15
The whole story:
CREATE TABLE algo.chengb_tmp
(
userid INT,
username varchar(100)
)
CREATE OR REPLACE FUNCTION algo.chengb_fun_test()
RETURNS INTEGER AS $$
DECLARE size INTEGER;
BEGIN
SELECT COUNT(*) INTO size FROM algo.chengb_tmp;
RETURN size;
END;
$$ LANGUAGE plpgsql STABLE;
SELECT algo.chengb_fun_test()
cheng
Obviously, your question does not show the whole story. I tested your function in PostgreSQL 9.1 and it works for me, as expected. No warning.
Possible explanations include:
A RULE on SELECT on the involved table tab that calls another function. (There are no triggers for SELECT)
A bug in the outdated PostgreSQL version 8.2.15, which might go away with an upgrade.
You over-simplified the question and abstracted the actual cause of the problem away.
Add more details in the question: your Version of PostgreSQL, the complete definition of table tab, the complete error message and its context.
As an aside, could be simplified:
CREATE OR REPLACE FUNCTION fun_test()
RETURNS INTEGER AS
$$
BEGIN
RETURN
(SELECT COUNT(*) FROM tab);
END;
$$ LANGUAGE plpgsql STABLE;
Ore even:
CREATE OR REPLACE FUNCTION fun_test()
RETURNS INTEGER AS
$$
SELECT count(*) FROM tab;
$$ LANGUAGE sql STABLE;
But that's probably not the point here.

Are there any way to execute a query inside the string value (like eval) in PostgreSQL?

I want to do like this:
SELECT (EVAL 'SELECT 1') + 1;
Are there any way to do like this (EVAL) in PostgreSQL?
If the statements you are trying to "eval" always return the same data type, you could write an eval() function that uses the EXECUTE mentioned by Grzegorz.
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute expression into result;
return result;
end;
$body$
language plpgsql
Then you could do something like
SELECT eval('select 41') + 1;
But this approach won't work if your dynamic statements return something different for each expression that you want to evaluate.
Also bear in mind that this opens a huge security risk by running arbitrary statements. If that is a problem depends on your environment. If that is only used in interactive SQL sessions then it isn't a problem.
NOTES
The language PLpgSQL syntax have many ways to say:
Y := f(X);
The EXECUTE clause is only for "dynamic execution" (less performance),
EXECUTE 'f(X)' INTO Y;
Use Y := f(X); or SELECT for execute static declarations,
SELECT f(X) INTO Y;
Use PERFORM statment when discard the results or to work with void returns:
PERFORM f(X);
I'd go with data type text since it's more flexible using casting operators like ::int if needed:
create or replace function eval( sql text ) returns text as $$
declare
as_txt text;
begin
if sql is null then return null ; end if ;
execute sql into as_txt ;
return as_txt ;
end;
$$ language plpgsql
-- select eval('select 1')::int*2 -- => 2
-- select eval($$ select 'a'||1||'b' $$) -- => a1b
-- select eval( null ) -- => null
I also added this and another eval( sql, keys_arr, vals_arr ) function supporting some custom key-value substitutions, e.g. for handy :param1 substitutions to postgres-utils
I am not sure if it suits you but PostgreSQL has EXECUTE statement.
Good idea. You can modify to perform direct expressions:
create or replace function eval(expression text) returns integer
as
$body$
declare
result integer;
begin
execute 'SELECT ' || expression into result;
return result;
end;
$body$
language plpgsql;
To run just type this:
SELECT eval('2*2');
Assuming that most sql queries are a part of a bigger system, there mostly will be cases where you form a query with your backend code and then execute it.
So if that’s the case for you, you can just use subselects or common table expressions that are put into your query string by the backend code before execution.
I have trouble coming up with cases where the solution you want works and my solution doesn’t, apart from not having any backend app, of course.