2015-05-01 149 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好!工作得很好。请使用答案按钮而不是评论,以便我可以分配点并将问题设置为已回答。 – lessj

+0

@a_horse_with_no_name是正确的。但是,我不确定为什么你在连接后不设置search_path。您在这里做的事听起来有点像很常见的多租户模式,您可以根据客户名称创建一系列相同的模式,然后在连接时将搜索路径设置为当前客户。将系统作为“测试”运行,在连接后设置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

而且不要引用语言名称。这是一个标识符。