#postgresql #scope #plpgsql #postgresql-9.5
#postgresql #область видимости #plpgsql #postgresql-9.5
Вопрос:
Я хотел бы создать функцию в PL / pgSQL с несколькими вложенными (или внутренними) функциями внутри нее. Таким образом, я могу разбить проблему на более мелкие части, но мои меньшие части не будут доступны за пределами этой функции.
Возможно ли это сделать в PL / pgSQL? Если да, то как?
Комментарии:
1. Зачем вам это делать? Мне кажется непрактичным. Вы можете разбить проблему на более мелкие части без большого количества функций или, если вам это действительно нужно, настроить привилегии для них.
2. @KamilGosciminski — вы упомянули, что «можете разбить проблему на более мелкие части без большого количества функций» — любопытно узнать, как вы могли бы это сделать?
Ответ №1:
Попробуйте:
CREATE OR REPLACE FUNCTION outer() RETURNS void AS $outer$
DECLARE s text;
BEGIN
CREATE OR REPLACE FUNCTION inner() RETURNS text AS $inner$
BEGIN
RETURN 'inner';
END;
$inner$ language plpgsql;
SELECT inner() INTO s;
RAISE NOTICE '%', s;
DROP FUNCTION inner();
END;
$outer$ language plpgsql;
В postgres 9.5 SELECT outer();
выводит
psql:/vagrant/f.sql:14: NOTICE: inner
РЕДАКТИРОВАТЬ: если вы не удалите внутреннюю
функцию в конце внешней функции, она останется видимой для остальной части базы данных.
Комментарии:
1. Пара вещей, на которые следует обратить внимание всем, кто пытается это сделать: вы не можете просто использовать
AS $$
как для внутренней, так и для внешней функции. Кроме того, если ваша функция имеет аргументы, вы должны передавать типы при удалении этой функции. И вы не можете вызывать свои функции вDECLARE
части внешней функции, потому что они еще не созданы. Просто создайте переменную и присвоите ей значение после создания вложенных функций.2. @Gregory: Еще один важный момент: если две параллельные транзакции попытаются вызвать эту функцию, вторая будет блокироваться на внутренней
CREATE
, пока первая не зафиксируется, из-за ограничения уникальности базы данных на имена функций. Вы можете обойти это, поместив внутреннюю функцию во временную схему вашего сеанса, т. Е. ИспользуяCREATE FUNCTION pg_temp.inner()
. Дополнительным преимуществом является то, что внутренние функции никогда не видны извне и автоматически очищаются после сеанса.3. @Gregory: Кстати,
DECLARE ... BEGIN ... END
блоки могут быть вложенными, поэтому вы можете выполнять свои объявления после создания внутренней функции4. Создание новой (внутренней) функции при каждом (внешнем) вызове функции кажется расточительным.
Ответ №2:
Вложенные функции не поддерживаются PLpgSQL. Эмуляция не имеет никакого смысла и непродуктивна.
Комментарии:
1. На первый взгляд эмуляция, описанная kiwi, сработала. Я протестировал его на небольших наборах данных, и он выглядел хорошо. Однако при работе с большими наборами данных всегда происходил сбой с очень странными ошибками блокировки. Я думаю, что любому, кто читает это, было бы лучше не пытаться заставить вложенные функции работать в PL / pgSQL.
2. @GregoryArenius: я немного поиграл с этим примером, он создает / заменяет функцию «inner» (в той же области, что и функция «outer») каждый раз, когда вызывается функция «outer», отсюда и странные ошибки. Это вообще не «внутренняя функция», и она также не имеет доступа к внутренней области «outer».
3. «Внутренняя функция» часто означает в языках программирования, что вы захотите получить доступ к переменным внешней функции из внутренней функции. Это не будет работать в примере apteryx.