pg_hint_plan
pg_hint_plan copied to clipboard
WA for issue with ignoring hints after catch any exception
We have an issues related to the invalid value of plpgsql_recurse_level counter in the PG13 and current master branch (Postgres 14).
The issue appears when we catch and handle any error inside any function/procedure. In this case the counter "plpgsql_recurse_level" becomes invalid because the method "pg_hint_plan_plpgsql_stmt_end" was not called. Therefore after catch any error until the end of the transaction we have the following issues:
PG13: pg_hint_plan does not process new hints for new requests PG14 (master): pg_hint_plan applies old hints from old queries to the new queries.
We investigated this issue and we were able to fix it using two options:
-
Change logic of counting nested levels and use ExecutorRun/ExecutorFinish hooks instead of pg_hint_plan_plpgsql_stmt_beg/pg_hint_plan_plpgsql_stmt_end hooks. Like in the original postgres code in the pg_stat_statement.c
-
Add flag to ignore plpgsql_recurse_level and parse hints for every statement independently of the level.
Option 1 - will change the output for one of the regression tests and require more code changes. Option 2 - we lost the ability to "push" a hint into the first statement inside function from the outside. This is can be WA by moving hint inside function itself.
This pull request is implementation of the "option 2" because in this case with disabled flag we don't change behaviour of old regression tests if someone hooked up with it. We add GUC property to ingore plpgsql_recurse_level
Here is cases to reproduce issues:
For PG13 branch + Postgresql 13
-- PG13
begin;
create or replace function pg_temp.func_with_exception(p_var varchar) returns numeric language plpgsql as
$$
begin
return p_var::numeric;
exception when others then null;
return -1;
end;
$$;
select pg_temp.func_with_exception('abc');
explain (COSTS false) /*+MergeJoin(d1 d2) */ with dual as (select 'x' as dummy) select * from dual d1, dual d2 where d1.dummy = d2.dummy;
-- Hint will be ignored
drop function pg_temp.func_with_exception(varchar);
rollback;
For master branch + Postgresql 14
-- PG14 case
begin;
select set_config('compute_query_id','off', true);
create or replace function pg_temp.test_bug_hint(p_var varchar) returns numeric language plpgsql as
$$
declare
begin
return p_var::numeric;
exception when others then return 1;
end;
$$;
select pg_temp.test_bug_hint('x');
-- Counter of nested level was changed to 1 due to invalid tracking during exception
explain with test /*+ MergeJoin(t1 t2) */ as (select 'x' val) select t1.val from test t1, test t2 where t1.val = t2.val;
explain with test2 as (select 'y' val) select t1.val from test t1, test t2 where t1.val <> t2.val;
-- MergeJoin will be applied to the both queries because current_hint_str wasn't cleanup due to the current nested level logic
rollback;