从函数返回的自定义类型的意外行为

时间:2013-04-10 11:44:50

标签: postgresql syntax types plpgsql postgresql-9.2

我创建了一个自定义类型

CREATE TYPE rc_test_type AS (a1 bigint);

和一个功能

CREATE OR REPLACE FUNCTION public.rc_test_type_function(test_table character varying, dummy integer)
  RETURNS rc_test_type AS
$BODY$ 
    DECLARE
    ret rc_test_type;
    query  text;
    BEGIN
    query := 'SELECT count(*) from '  || test_table    ;
    EXECUTE query into ret.a1;  
    RETURN ret;
END $BODY$
  LANGUAGE plpgsql VOLATILE

如果我跑

SELECT * FROM rc_test_type_function('some_table', 1);

我得到了

"a1"
1389

到目前为止一切顺利。

如果我跑

SELECT p FROM (SELECT rc_test_type_function('some_table', s.step) AS p 
FROM  some_other_table s) foo;

我得到了

"p"
"(1389)"
"(1389)"

因为'some_other_table'只有两条记录。细

但是如果我尝试

SELECT p.a1 FROM (select rc_test_type_function('some_table', s.step) AS p
FROM some_other_table s) foo;

我收到错误

  

在子查询中缺少FROM子句条目表»p <<

我觉得奇怪,因为子查询没有改变。

两个问题:

  1. 任何人都可以解释发生了什么吗?
  2. 如何从返回的数组中提取字段值a1

1 个答案:

答案 0 :(得分:2)

围绕复合类型使用括号

SELECT (p).a1
FROM   (SELECT rc_test_type_function('some_table', s.step) AS p
        FROM   some_other_table s
       ) foo;

即使您的类型只有一个列仍然是复合类型 - 具有自己的列名称。没有多大意义,但这就是你建造它的方式 (您可能只想使用简单类型或DOMAIN代替。)

引用the manual here

  

(compositecol).somefield
  (mytable.compositecol).somefield

     

此处需要括号表示compositecol是列名而不是a   表名,或mytable是表名而不是第二种情况下的模式名。

正常功能

省略具有复合类型的零件,您的功能将更安全,更简单,更快

CREATE OR REPLACE FUNCTION foo(test_table varchar, dummy int, OUT p bigint)
  AS
$func$ 
BEGIN
   EXECUTE format('SELECT count(*) from %I', test_table)  -- !avoid SQLi!
   INTO p;  
END
$func$ LANGUAGE plpgsql;
  • 避免使用动态SQL注入SQL!
  • 在这种情况下,OUT parameter简化了语法。您根本不需要DECLARE子句,也不需要RETURN

更好

CREATE OR REPLACE FUNCTION foo(test_table regclass, dummy int, OUT p bigint)
  AS
$func$ 
BEGIN
   EXECUTE 'SELECT count(*) from ' || test_table
   INTO p;  
END
$func$ LANGUAGE plpgsql;
  • 通过使用object identifier regclass,这也适用于模式限定的表名。并且SQLi开始时不可能。如果表名是非法的,则该函数会立即失败,并在自动转换为text时自动引用。