1

我创建了一个自定义类型

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;

我得到错误

表»p«的子查询中缺少 FROM 子句条目

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

两个问题:

  1. 谁能解释发生了什么?
  2. 如何a1从返回的数组中提取字段值?
4

1 回答 1

3

在复合类型周围使用括号

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

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

在这里引用手册

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

此处需要括号来显示这compositecol是列名而不是表名,或者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参数简化了语法。您根本不需要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;
  • 通过使用对象标识符regclass,这也适用于模式限定的表名。并且 SQLi 是不可能开始的。如果表名非法,该函数将立即失败,并在转换为text自动时自动引用。
于 2013-04-10T14:10:40.047 回答