在相关函数调用中保留 PostgreSQL 架构标识符

Preserve PostgreSQL schema identifier in dependent function calls

我有一个存储过程 RETURNS record:

CREATE OR REPLACE FUNCTION r_rpt_prov_summary() 
RETURNS record 
AS $$ 
  DECLARE
    _fields             record;
  BEGIN
    SELECT INTO _fields
      0::integer AS customers,
      0::integer AS customers_active;

    SELECT INTO _fields.customers count(*) FROM customer;
    SELECT INTO _fields.customers_active count(*) FROM customer WHERE active = 't';

    RETURN _fields;
  END
$$ LANGUAGE 'plpgsql';

但是,为了查询它,我必须明确枚举返回的列和类型:

SELECT * FROM r_rpt_prov_summary() AS (a integer, b integer);

为了使其适合 MVC 框架,其本质上想要查询表,我将其包装在 SQL 函数中 RETURNS TABLE:

CREATE OR REPLACE FUNCTION rpt_prov_summary() 
RETURNS TABLE (
        customers               integer,
        customers_active        integer
) AS $$
  SELECT * FROM r_rpt_prov_summary() AS (customers integer, customers_active integer);
$$ LANGUAGE 'sql';

只要两个函数驻留在相同的模式或 search_path space 中,这就非常有效。但是,在这种情况下,它们存在于自己的非标准模式中,因此我必须将外部函数查询为 myschema.rpt_prov_summary(),即

SELECT * FROM myschema.rpt_prov_summary();

如果我的架构和搜索路径设置为 public:

,这将不起作用
test=> SELECT * FROM myschema.rpt_prov_summary();
ERROR:  function r_rpt_prov_summary() does not exist
LINE 2:   SELECT * FROM r_rpt_prov_summary() AS (customers integer, ...
                        ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

自然地,您的想法会转向在查询执行之前调用 SET SCHEMA 'myschema'SET search_path TO myschema。我的也是。问题是它不适用于我调用的环境。 MVC 框架使用准备好的语句来构造查询,而 PostgreSQL 不赞成这样的事情:

SET search_path TO myschema;SELECT * FROM rpt_prov_summary();

也就是说:

< 2016-03-14 20:50:46.410 EDT >ERROR: cannot insert multiple commands into a prepared statement

所以,那是行不通的。将模式作为参数提供给外部函数也行不通;我只是在 MVC 框架的约束下没有这种灵活性,它想要查询普通的旧表或像表一样的东西。

current_schema 标识客户端会话的当前架构,因此这不会有帮助。已经试过了:

CREATE OR REPLACE FUNCTION rpt_prov_summary() 
RETURNS TABLE (
    customers       integer,
    customers_active    integer
) AS $$
  BEGIN
    RAISE NOTICE 'Current schema: %', current_schema;
    RETURN QUERY EXECUTE 'SELECT * FROM ' || current_schema || '.r_rpt_prov_summary() AS (customers integer, customers_active integer)';
  END
$$ LANGUAGE 'plpgsql';

没有骰子 - Current schema: public,如预期。

有没有办法以某种方式捕获外部调用的架构 space 并将其传播到封装的查询中?

1.

I have a stored procedure ...

不,你不知道。你有一个功能,它几乎但不完全相同。 Postgres 目前不支持存储过程。

2.

彻底简化。使用一个带有 OUT 参数的简单 SQL 函数,而不是两个嵌套函数:

CREATE OR REPLACE FUNCTION myschema.r_rpt_prov_summary(  -- schema-qualify!
          OUT _customers integer
        , OUT _customers_active integer) AS
$func$
   SELECT count(*)::int
        , count(*) FILTER (WHERE active)::int  -- requires Postgres 9.4+
   FROM   myschema.customer;                   -- schema-qualify!
$func$ LANGUAGE sql;                           -- don't quote the language name

致电:

SELECT * FROM myschema.rpt_prov_summary();

独立于您当前的 search_path 设置工作。

注意RETURNS TABLE()RETURNS record之间的细微差别(我的版本也是RETURNS record,但我在声明OUT参数后没有明确指定!):后面总是returns 恰好 1 行(Postgres 知道并可以依赖它),而前者可以 return 0 - n 行.

3.

可以 以多种不同方式设置 search_path ,甚至可以作为函数本身的本地设置 - 如有必要:

  • How does the search_path influence identifier resolution and the "current schema"

回答您的实际问题

Is there any way to somehow capture the schema space of the outer call and propagate that into the encapsulated query?

CREATE OR REPLACE FUNCTION myschema.r_rpt_prov_summary(OUT _customers integer
                                                     , OUT _customers_active integer) AS 
$func$ 
BEGIN
   SELECT INTO _customers, _customers_active
          count(*)::int, count(*) FILTER (WHERE active)::int  
   FROM   <b>customer;  -- no schema-qualification</b>
END
$func$  LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION myschema.rpt_prov_summary() 
  RETURNS TABLE (customers int, customers_active int) AS
$func$
   SELECT * FROM myschema.r_rpt_prov_summary();
$func$ LANGUAGE sql <b>SET search_path = myschema</b>;  -- set the search_path here

search_path 传播到嵌套函数 - 正是您要找的。

或者只是模式限定标识符无处不在(包括函数名称)是明确的。