Как получить оператор, вызывающий функцию, изнутри самой функции?

Допустим, у меня есть функция show_files(IN file text, IN suffix text, OUT statement text). На следующем шаге вызывается функция:

 SELECT * FROM show_files(file := 'example', suffix := '.png');

Мой вопрос: есть ли какое-либо решение, которое я мог бы получить, вызвав эту функцию изнутри этой функции?

Я имею в виду, что после запуска SELECT вывод функции (OUT statement text) должен быть: 'SELECT * FROM show_files(file := 'example', suffix := '.png');', или можно ли присвоить этот оператор переменной внутри функции?

Мне нужна функциональность, подобная TG_NAME, TG_OP и т. д. в триггерных процедурах.

Может быть, можно получить этот оператор из SELECT current_query FROM pg_stat_activity?

Когда я пытаюсь использовать его внутри функции, у меня есть пустая запись:

CREATE OR REPLACE FUNCTION f_snitch(text)
  RETURNS text AS
$BODY$
declare
rr text;
BEGIN
    RAISE NOTICE '.. from f_snitch.';
    -- do stuff
    SELECT current_query  into rr FROM pg_stat_activity 
    WHERE current_query ilike 'f_snitch';
    RETURN rr;
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

Будем рады любой помощи и предложениям!


person Borys    schedule 26.10.2012    source источник
comment
Вы можете получить запрос верхнего уровня с помощью SELECT current_query() ... но это не даст вам непосредственный вызывающий объект, а даст вам оператор пользователя верхнего уровня. Я не знаю никакого способа получить немедленный оператор вызова; на самом деле нет стека для изучения.   -  person Craig Ringer    schedule 27.10.2012
comment
Спасибо, но current_query() дает мне результат: 'СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ФУНКЦИЮ f_snitch(text)', и я хотел бы иметь: 'SELECT f_snitch('foo')';   -  person Borys    schedule 30.10.2012


Ответы (2)


TG_NAME и друзья — это специальные переменные, которые существуют только для триггерных функций. Обычные функции plpgsql не имеют ничего подобного. У меня только что закончились идеи, как вы могли бы получить это внутри функции вызываемой в plpgsql.

Вы можете добавить RAISE NOTICE в свою функцию, чтобы получить нужную информацию

CREATE OR REPLACE FUNCTION f_snitch(text)
  RETURNS text LANGUAGE plpgsql AS
$func$
BEGIN
    RAISE NOTICE '.. from f_snitch.';
    -- do stuff
    RETURN 'Snitch says hi!';
END
$func$;

Вызов:

SELECT f_snitch('foo')

В дополнение к результату это возвращает уведомление:

NOTICE:  .. from f_snitch.

Не устраивает в двух отношениях:

  1. Заявление о вызове отсутствует в уведомлении.
  2. В уведомлении отсутствует CONTEXT.

Для 1. вместо этого вы можете использовать RAISE LOG (или настроить свой кластер на регистрацию УВЕДОМЛЕНИЙ, чего я обычно не делаю, слишком многословно для меня). При стандартных настройках вы получаете дополнительную строку с STATEMENT в журнале базы данных:

LOG:  .. from f_snitch.
STATEMENT:  SELECT f_snitch('foo')

Для 2. просмотрите этот связанный вопрос на dba.SE. CONTEXT будет выглядеть так:

CONTEXT:  SQL statement "SELECT f_raise('LOG', 'My message')"
    PL/pgSQL function "f_snitch" line 5 at PERFORM
person Erwin Brandstetter    schedule 26.10.2012

Хорошо, я понял!

CREATE OR REPLACE FUNCTION f_snitch(text)
  RETURNS setof record AS
$BODY$
BEGIN
   RETURN QUERY
    SELECT current_query 
    FROM pg_stat_activity 
    <strike>ORDER BY length(current_query) DESC LIMIT 1;</strike>
    where current_query ilike 'select * from f_snitch%';
    -- much more reliable solution

END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

  select * from f_snitch('koper') AS (tt text);

И вот результат:

введите здесь описание изображения

Это, вероятно, не 100% надежное решение, но для небольших систем (для нескольких пользователей) вполне нормально.

person Borys    schedule 30.10.2012
comment
Это приведет к случайным, причудливым и неожиданным результатам при наличии нескольких одновременных сеансов. Вам нужно будет отфильтровать по pid = pg_backend_pid(), чтобы исключить другие сеансы. Я также не уверен, что это действительно правильно. - person Craig Ringer; 31.10.2012
comment
Я с тобой согласен. Вот почему я изменил предложение where и добавил несколько новых строк в функцию, которая улавливает результат этого. В моем случае — учитывая систему, которую я разрабатываю — не нужно беспокоиться о нескольких сеансах, выполняющих эту функцию. Это небольшой, специализированный софт для нескольких человек, но я должен согласиться с более крупными проектами - операции pid будут необходимы. - person Borys; 18.11.2012