2

我在具有相同函数名称的单个数据库中有两个相似的模式。每个模式都由与模式名称匹配的角色拥有。

我有关于嵌套函数的函数名称解析的问题。我原以为外部函数会在同一个模式中调用内部函数,但事实并非如此!该名称是在运行时根据 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;
产品架构:
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,但我正在寻找更直接的方法。

4

1 回答 1

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_pathper 函数。您可以通过这种方式设置配置参数

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 放在最后,所以临时模式中的对象不能隐藏持久对象。(但这不适用于函数。)类似于函数手册中SECURITY DEFINER的解释。

建议依靠用户设置正确的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按照建议提供。在我报告了一个错误后,这在 2010 年得到了修复。

详细信息search_path

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

于 2015-05-02T13:32:33.843 回答