2015-05-01 4 views
2

У меня есть две аналогичные схемы в одной базе данных с теми же именами функций. Каждая схема принадлежит роли, которая соответствует имени схемы.Разрешение имени функции PL/pgSQL с вложенными функциями

У меня есть проблемы с разрешением имени функции с вложенными функциями. Я ожидал, что внешняя функция вызовет внутренние функции внутри одной схемы, но это не так! Имя разрешено динамически основано на пути search_path во время выполнения, которое делает некоторые чувства, но не так, как я.

Вот тестовая версия. Скажем, например, что схемы и роли называются test и prod.

Тест схема:
CREATE ROLE test NOLOGIN; 
CREATE SCHEMA test AUTHORIZATION test; 

CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT 
AS $BODY$ 
BEGIN 
    RETURN 'test function'; 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION test.inner_func() OWNER TO test; 

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION test.outer_func() OWNER TO test; 
Prod схема:
CREATE ROLE prod NOLOGIN; 
CREATE SCHEMA prod AUTHORIZATION prod; 

CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT 
AS $BODY$ 
BEGIN 
    RETURN 'prod function'; 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION prod.inner_func() OWNER TO prod; 

CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION prod.outer_func() OWNER TO prod; 
Тестовые:
SET search_path=test,public;  
SELECT outer_func(); 
> test function 

SELECT prod.outer_func(); 
> test function <<<---- was expecting prod function 

SET search_path=prod,public; 
SELECT prod.outer_func(); 
> prod function

Тест показывает, что имена функций решаются динамически на основе search_path во время выполнения. Есть ли способ связать внутреннюю функцию в рамках схемы?

Я могу получить такое поведение, используя функции SECURITY DEFINER с динамическим SQL и CURRENT_USER, но я ищу что-то более простое.

+0

Вы можете установить требуемый путь поиска как атрибут функции (используя 'SET' в определении функции). Btw: имя языка является идентификатором, не помещайте его в одинарные кавычки. Это устаревший синтаксис, который будет удален в будущем. –

+0

@ a_horse_with_no_name Nice! работайте так хорошо. Пожалуйста, используйте кнопку ответа вместо комментария, чтобы я мог назначать точки и задавать вопрос в соответствии с ответом. – lessj

+0

@a_horse_with_no_name верное. Но я не уверен, почему вы просто не устанавливаете путь search_path после подключения. То, что вы здесь делаете, похоже на довольно распространенный шаблон с несколькими арендаторами, где вы создаете серию идентичной схемы на основе имени клиента, а затем, когда вы подключаетесь, вы устанавливаете путь поиска к текущему клиенту. Запуск системы как «тест» устанавливает путь поиска после подключения и т. Д. –

ответ

0

Чистое решение является либо схемы-квалифицироваться функции:

CREATE OR REPLACE FUNCTION test.outer_func() 
    RETURNS SETOF text AS 
$func$ 
BEGIN 
    RETURN QUERY SELECT test.inner_func(); 
END 
$func$ LANGUAGE plpgsql; -- no quotes!

Или вы явно установить search_path за функции. Вы можете set configuration parameters this way:

CREATE OR REPLACE FUNCTION test.outer_func() 
    RETURNS SETOF text AS 
$func$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$func$ LANGUAGE plpgsql SET search_path = test, pg_temp;

Настройка search_path для ваших нужд, возможно, добавить public в список. Я положил pg_temp в конец, поэтому объекты во временной схеме не могут скрывать объекты, которые сохраняются. (Но это не относится к функциям.) Как и в том, что объясняется в the manual for SECURITY DEFINER functions.

Я не советуют полагаться на настройки правильного search_path пользователя. Это имело бы смысл только для «публичных» функций, это не соответствовало бы вашему дизайну. Зачем создавать отдельные функции, а затем все еще нужно полагаться на пользовательские настройки? У вас может быть одна функция в общедоступной схеме, но я бы не получил этот маршрут в любом случае. Очень запутанный и подверженный ошибкам.

Кроме того, PL/pgSQL выполняет внутренние операции, такие как подготовленные операторы. каждый раз, когда вы меняете путь search_path, все «подготовленные» утверждения из функций plpgsql должны быть де-распределены, что не помогает оптимизировать производительность.

На самом деле, ваш тест на вопрос работает только если вы установите search_path первый:

SET search_path=test,public; 

Иначе вы получите сообщение об ошибке при попытке создать

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
... 
ERROR: function inner_func() does not exist 

Проверка синтаксиса выполняется против текущего search_path при создании время - , если только вы не указали search_path, как предложено. That was fixed 2010 after I reported a bug.

Детали для search_path:

не экранирует имя языка. Это идентификатор.