I have a table in the database:
create table store (
...
n_status integer not null,
t_tag varchar(4)
t_name varchar,
t_description varchar,
dt_modified timestamp not null,
...
);
In my stored function I need to execute the same select
against this table multiple times:
select * from store
where n_place_id = [different values]
and t_tag is not null
and n_status > 0
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified desc
limit n_max;
Here, t_search
and n_max
are parameters into the stored function. I thought it would make sense to use a prepared statement for this, but I'm running into strange problems. Here's what I have:
create or replace function fn_get_data(t_search varchar, n_max integer)
returns setof store as
$body$
declare
resulter store%rowtype;
mid integer;
begin
prepare statement prep_stmt(integer) as
select *
from store
where n_place_id = $1
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified
limit n_max;
for mid in
(select n_place_id from ... where ...)
loop
for resulter in
execute prep_stmt(mid)
loop
return next resulter;
end loop;
end loop;
end;$body$
language 'plpgsql' volatile;
However when I actually run the function with
select * from fn_get_data('', 30)
I receive this error:
ERROR: column "t_search" does not exist
LINE 3: and (t_name ~* t_search or t_description ~* t_search)
^
QUERY: prepare prep_stmt(integer) as
select * from store where n_status > 0 and t_tag is not null and n_museum = $1
and (t_name ~* t_search or t_description ~* t_search)
order by dt_modified desc limit maxres_free
Ok, maybe it doesn't like external variables in the prepared statement, so I changed this to be
prepare prep_stmt(integer, varchar, integer) as
select * from store where n_status > 0 and t_tag is not null and n_museum = $1
and (t_name ~* $2 or t_description ~* $2)
order by dt_modified desc limit $3
...
for resulter in
execute prep_stmt(mid, t_search, n_max)
...
This time I get a different error:
ERROR: function prep_stmt(integer, character varying, integer) does not exist
LINE 1: SELECT prep_stmt(mid, t_search, n_max)
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: SELECT prep_stmt(mid, t_search, n_max)
What am I missing here?
EDIT I added the relevant table structure at the top.
Looks to me like the PL/PgSQL EXECUTE
for dynamic SQL trumps the regular SQL EXECUTE
for prepared statements.
Code:
create or replace function prep_test() returns void as $$
begin
PREPARE do_something AS SELECT 1;
EXECUTE do_something;
end;
$$ LANGUAGE 'plpgsql';
Test:
regress=# select prep_test(1);
ERROR: column "do_something" does not exist
LINE 1: SELECT do_something
^
QUERY: SELECT do_something
CONTEXT: PL/pgSQL function "prep_test" line 4 at EXECUTE statement
outside PL/PgSQL it works fine:
regress=# EXECUTE do_something;
?column?
----------
1
(1 row)
I'm not sure how you'd execute a prepared statement within PL/PgSQL.
Out of interest, why are you trying to use prepared statements within PL/PgSQL? Plans are prepared and cached for PL/PgSQL anyway, it happens automatically.
There is a way to EXECUTE
a prepared statement in a function, but like the accepted answer said, you typically don't wan't to do this in a function because the function already stores its plan.
That being said, there are still use cases where you do need to use a prepared statement in a function. My use case for this is when using multiple schemas for different users where the schemas contain tables that are similarly named and you want to use the same function to access one of these tables based off of what the search_path
is set to. In this situation, because of the way the function stores its plan, using the same function after changing the search_path
causes things to break. There are two solutions to this problem that I've stated. The first is to use EXECUTE '<Your query as a string here>'
. But this can get very ugly for large queries, hence the reason to use the second method, which involves a PREPARE
.
So with the background as to 'why' you'd want to do this out of the way, here is the how:
CREATE OR REPLACE FUNCTION prep_test()
RETURNS void AS $$
BEGIN
PREPARE do_something AS SELECT 1;
EXECUTE 'EXECUTE do_something;';
END;
$$ LANGUAGE plpgsql;
Though it will probably be in your best interests to add some protections to keep it from breaking. Something like:
CREATE OR REPLACE FUNCTION prep_test()
RETURNS void AS $$
BEGIN
IF (SELECT count(*) FROM pg_prepared_statements WHERE name ilike 'do_something') > 0 THEN
DEALLOCATE do_something;
END IF;
PREPARE do_something AS SELECT 1;
EXECUTE 'EXECUTE do_something;';
DEALLOCATE do_something;
END;
$$ LANGUAGE plpgsql;
Again, those who think that they want to do this, usually probably shouldn't, but for those cases where it is needed, this is how you do it.
You could use an EXECUTE statement like this in PLPGSQL:
select magicvalue into str_execute from magicvalues where magickey = ar_requestData[2];
EXECUTE str_execute into str_label USING ar_requestData[3], ar_requestData[4]::boolean, ar_requestData[5]::int, ar_requestData[6];
This is code I use in my application. ar_requestData is an array with text values.
In the table magicvalues do I store things like prepared statements.
The select statement is for example:
insert into classtypes(label, usenow, ranking, description) values($1,$2,$3,$4) returning label'
With kind regards,
Loek Bergman
PREPARE statement is not allowed within plpgsql. You can splice all the statements inside a function and use dynamic execute after then. Here is an example.
create or replace function sp_test(f_total int) returns void as $ytt$
declare v_sql text;
declare i int;
begin
v_sql:='prepare ytt_s1 (int,timestamp) as select * from tbl1 where id = $1 and log_time = $2;';
while i < f_total
loop
v_sql:=v_sql||'execute ytt_s1('||i||',now());';
i := i + 1;
end loop;
v_sql:=v_sql||'deallocate ytt_s1;';
execute v_sql;
end;
$ytt$ language plpgsql;