BUG #18767: Inconsistency in result of a Plpgsql GET DIAGNOSTICS PG_CONTEXT instruction. - Mailing list pgsql-bugs

From PG Bug reporting form
Subject BUG #18767: Inconsistency in result of a Plpgsql GET DIAGNOSTICS PG_CONTEXT instruction.
Date
Msg-id 18767-e36a4af08c98f883@postgresql.org
Whole thread Raw
Responses Re: BUG #18767: Inconsistency in result of a Plpgsql GET DIAGNOSTICS PG_CONTEXT instruction.
List pgsql-bugs
The following bug has been logged on the website:

Bug reference:      18767
Logged by:          Philippe BEAUDOIN
Email address:      phb.emaj@free.fr
PostgreSQL version: 17.2
Operating system:   Linux
Description:

I use GET DIAGNOSTICS PG_CONTEXT in some sensitive plpgsql functions that
are declared SECURITY DEFINER in order to be sure that the caller belongs to
a list of known callers. The returned stack contains the list of called
functions, with their schema prefix and their parameters format.
But I discovered that when the schema holding these functions is created
while it is in the user's search_path, the returned stack doesn't contain
the schema prefix anymore for the caller functions.
Here is a small test case that reproduces the issue.

A psql script :
select version();
drop schema if exists tst cascade;
create schema tst;
create function tst.caller1() returns text language plpgsql as 
$$ BEGIN return tst.caller2(); END; $$;
create function tst.caller2() returns text language plpgsql as 
$$ BEGIN return tst.called(); END; $$;
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
  DECLARE
    v_stack                  TEXT;
  BEGIN
    GET DIAGNOSTICS v_stack = PG_CONTEXT;
    IF v_stack NOT LIKE '%tst.caller2()%' THEN
      RAISE WARNING 'Beware: the calling function is not the expected
one!';
    END IF;
    RETURN v_stack;
  END;
$$;
set search_path = tst;
show search_path;
select caller1() as stack_in_called;
reset search_path;
show search_path;
select tst.caller1() as stack_in_called;

A simple shell script :
echo "=========================================================="
echo "                    The standart run"
echo "=========================================================="
psql  -a -f test_stack.sql
echo "=========================================================="
echo "The issue: a search_path is set before creating structures"
echo "=========================================================="
psql  -a -c "SET search_path = tst;" -f test_stack.sql

And the output result :
==========================================================
                    The standart run
==========================================================
select version();
                                                version
                          
-------------------------------------------------------------------------------------------------------
 PostgreSQL 17.2 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu
13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit
(1 row)

drop schema if exists tst cascade;
psql:test_stack.sql:2: NOTICE:  drop cascades to 3 other objects
DETAIL:  drop cascades to function tst.caller1()
drop cascades to function tst.caller2()
drop cascades to function tst.called()
DROP SCHEMA
create schema tst;
CREATE SCHEMA
create function tst.caller1() returns text language plpgsql as 
$$ BEGIN return tst.caller2(); END; $$;
CREATE FUNCTION
create function tst.caller2() returns text language plpgsql as 
$$ BEGIN return tst.called(); END; $$;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
  DECLARE
    v_stack                  TEXT;
  BEGIN
    GET DIAGNOSTICS v_stack = PG_CONTEXT;
    IF v_stack NOT LIKE '%tst.caller2()%' THEN
      RAISE WARNING 'Beware: the calling function is not the expected
one!';
    END IF;
    RETURN v_stack;
  END;
$$;
CREATE FUNCTION
set search_path = tst;
SET
show search_path;
 search_path 
-------------
 tst
(1 row)

select caller1() as stack_in_called;
                     stack_in_called                      
----------------------------------------------------------
 PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
 PL/pgSQL function tst.caller2() line 1 at RETURN        +
 PL/pgSQL function tst.caller1() line 1 at RETURN
(1 row)

reset search_path;
RESET
show search_path;
   search_path   
-----------------
 "$user", public
(1 row)

select tst.caller1() as stack_in_called;
                     stack_in_called                      
----------------------------------------------------------
 PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
 PL/pgSQL function tst.caller2() line 1 at RETURN        +
 PL/pgSQL function tst.caller1() line 1 at RETURN
(1 row)

==========================================================
The issue: a search_path is set before creating structures
==========================================================
SET search_path = tst;
SET
select version();
                                                version
                          
-------------------------------------------------------------------------------------------------------
 PostgreSQL 17.2 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu
13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit
(1 row)

drop schema if exists tst cascade;
psql:test_stack.sql:2: NOTICE:  drop cascades to 3 other objects
DETAIL:  drop cascades to function caller1()
drop cascades to function caller2()
drop cascades to function called()
DROP SCHEMA
create schema tst;
CREATE SCHEMA
create function tst.caller1() returns text language plpgsql as 
$$ BEGIN return tst.caller2(); END; $$;
CREATE FUNCTION
create function tst.caller2() returns text language plpgsql as 
$$ BEGIN return tst.called(); END; $$;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
  DECLARE
    v_stack                  TEXT;
  BEGIN
    GET DIAGNOSTICS v_stack = PG_CONTEXT;
    IF v_stack NOT LIKE '%tst.caller2()%' THEN
      RAISE WARNING 'Beware: the calling function is not the expected
one!';
    END IF;
    RETURN v_stack;
  END;
$$;
CREATE FUNCTION
set search_path = tst;
SET
show search_path;
 search_path 
-------------
 tst
(1 row)

select caller1() as stack_in_called;
psql:test_stack.sql:24: WARNING:  Beware: the calling function is not the
expected one!
                     stack_in_called                      
----------------------------------------------------------
 PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
 PL/pgSQL function caller2() line 1 at RETURN            +
 PL/pgSQL function caller1() line 1 at RETURN
(1 row)

reset search_path;
RESET
show search_path;
   search_path   
-----------------
 "$user", public
(1 row)

select tst.caller1() as stack_in_called;
psql:test_stack.sql:27: WARNING:  Beware: the calling function is not the
expected one!
                     stack_in_called                      
----------------------------------------------------------
 PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
 PL/pgSQL function caller2() line 1 at RETURN            +
 PL/pgSQL function caller1() line 1 at RETURN
(1 row)


pgsql-bugs by date:

Previous
From: David Rowley
Date:
Subject: Re: BUG #18766: not exists sometimes gives too few records
Next
From: Jan Kort
Date:
Subject: Re: BUG #18766: not exists sometimes gives too few records