I am having troubles understanding how the query plan caching works for pl/pgsql.
I want to built all-in-one queries with JOIN
s and IF
s, so I will have multiple and different query parameters, and I will be searching in more that one tables.
At first I thought that using pl/pgsql will produce a different plan for each parameters combination, that is not the case, because I have more than one tables
SQL commands that appear directly in a PL/pgSQL function must refer to the same tables and columns on every execution; that is, you cannot use a parameter as the name of a table or column in an SQL command. To get around this restriction, you can construct dynamic commands using the PL/pgSQL EXECUTE statement — at the price of performing new parse analysis and constructing a new execution plan on every execution. from here
Performing a new analysis every time can slow things down I guess. If I dont use an EXECUTE
then
If the statement has no parameters, or is executed many times, the SPI manager will consider creating a generic plan that is not dependent on specific parameter values, and caching that for re-use. Typically this will happen only if the execution plan is not very sensitive to the values of the PL/pgSQL variables referenced in it. If it is, generating a plan each time is a net win. from here
Should I use a generic plan then? Is it faster, or is it slower because there is no planning each time ? At least they are cached. My queries are sensitive to their variables, because they are dynamic, but what
If it is, generating a plan each time is a net win.
actually means? That using EXECUTE
/plan each time, is better or worst than a generic one? "net win" confuses me.
If a generic plan is inaccurate and EXECUTE
/planning each time is slower, then why bother using pl/pgsql? I can write a simple query with a couple of ifs then.
Bottom line is, I cannot conclude if EXECUTE/plan each time
is better or worst than generic cached plan
in terms of speed and plan caching. Please explain and advice, I am confused.
For reference, this is what I am creating. Works as it is now, but more IFs will be added for mytables
and mywhere
DROP FUNCTION IF EXISTS __aa(ii int, fk int);
CREATE FUNCTION __aa(ii int, fk int) RETURNS TABLE(id INTEGER,val text, fd integer) AS $$
DECLARE
myt text;
mytables text;
mywhere text;
BEGIN
mytables := 'dyn_tab2';
mywhere := 'dyn_tab2.id=$1';
IF fk IS NOT NULL
THEN
mywhere := mywhere || 'AND dyn_tab2.fk_id=$2';
END IF;
RETURN QUERY EXECUTE format('
SELECT dyn_tab2.id, dyn_tab2.value, dyn_tab2.fk_id
FROM %I WHERE ' ||mywhere,
mytables)
USING ii, fk;
END;
$$
LANGUAGE 'plpgsql';
Thanks