6

考虑这两个 PostgreSQL 函数:

CREATE OR REPLACE FUNCTION f_1 (v1 INTEGER, v2 OUT INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION f_2 (v1 INTEGER)
RETURNS TABLE(v2 INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

在任何“普通”的过程 SQL 语言(例如 Transact-SQL)中,这两种类型的函数将是完全不同的。f_1实际上将是一个过程,而f_2将是一个表值函数。在 SQL Server 中,后者是INFORMATION_SCHEMA.ROUTINES这样返回的:

SELECT r.routine_schema, r.routine_name
FROM   information_schema.routines r
WHERE  r.routine_type = 'FUNCTION'
AND    r.data_type = 'TABLE'

但是,在 PostgreSQL 中,这不起作用。f_1以下查询表明和的签名本质上没有区别f_2

SELECT r.routine_name, r.data_type, p.parameter_name, p.data_type
FROM   information_schema.routines r
JOIN   information_schema.parameters p
USING (specific_catalog, specific_schema, specific_name);

以上产生:

routine_name | data_type | parameter_name | data_type
-------------+-----------+----------------+----------
f_1          | integer   | v1             | integer
f_1          | integer   | v2             | integer
f_2          | integer   | v1             | integer
f_2          | integer   | v2             | integer

当我从函数返回多个列时,情况并没有好转,在这种情况下,我什至不再有“正式”返回类型。只是record

CREATE OR REPLACE FUNCTION f_3 (v1 INTEGER, v2 OUT INTEGER, v3 OUT INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION f_4 (v1 INTEGER)
RETURNS TABLE(v2 INTEGER, v3 INTEGER)
AS $$
BEGIN
    v2 := v1;
END
$$ LANGUAGE plpgsql;

... 我去拿:

routine_name | data_type | parameter_name | data_type
-------------+-----------+----------------+----------
f_3          | record    | v1             | integer
f_3          | record    | v2             | integer
f_3          | record    | v3             | integer
f_4          | record    | v1             | integer
f_4          | record    | v2             | integer
f_4          | record    | v3             | integer

如果来自其他数据库,显然词汇签名的意图是完全不同的。作为一个 Oracle 人,我希望PROCEDURES有副作用,而FUNCTIONS没有任何副作用(除非在自主事务中)并且可以安全地嵌入到 SQL 中。我知道 PostgreSQL 巧妙地将所有函数都视为表,但我认为OUT在任何查询中将参数设计为表列并不是一个好主意...

我的问题是:

声明函数的两种方式之间有任何形式上的区别吗?如果有,我怎样才能从INFORMATION_SCHEMA或 中发现它PG_CATALOG

4

3 回答 3

3

\df public.f_*做这个

select
    n.nspname as "Schema",
    p.proname as "Name",
    pg_catalog.pg_get_function_result(p.oid) as "Result data type",
    pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
    case
        when p.proisagg then 'agg'
        when p.proiswindow then 'window'
        when p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype then 'trigger'
        else 'normal'
    end as "Type"
from
    pg_catalog.pg_proc p
    left join
    pg_catalog.pg_namespace n on n.oid = p.pronamespace
where
    p.proname ~ '^(f_.*)$'
    and n.nspname ~ '^(public)$'
order by 1, 2, 4;

返回这个

                                          List of functions
 Schema | Name |       Result data type        |            Argument data types             |  Type  
--------+------+-------------------------------+--------------------------------------------+--------
 public | f_1  | integer                       | v1 integer, OUT v2 integer                 | normal
 public | f_2  | TABLE(v2 integer)             | v1 integer                                 | normal
 public | f_3  | record                        | v1 integer, OUT v2 integer, OUT v3 integer | normal
 public | f_4  | TABLE(v2 integer, v3 integer) | v1 integer                                 | normal
(4 rows)

要删除函数,必须传递其输入ININOUT)参数数据类型。然后我猜函数名和它的输入参数数据类型确实形成了它的签名。要更改返回的数据类型,必须先将其删除并重新创建。

于 2014-07-01T14:46:40.110 回答
2

该标志似乎包含有关函数是否返回集合(即表)的提示:pg_catalog.pg_proc.proretset

SELECT   r.routine_name, r.data_type, p.parameter_name, 
         p.data_type, pg_p.proretset
FROM     information_schema.routines r
JOIN     information_schema.parameters p
USING   (specific_catalog, specific_schema, specific_name)
JOIN     pg_namespace pg_n
ON       r.specific_schema = pg_n.nspname
JOIN     pg_proc pg_p
ON       pg_p.pronamespace = pg_n.oid
AND      pg_p.proname = r.routine_name
WHERE    r.routine_schema = 'public'
AND      r.routine_name IN ('f_1', 'f_2', 'f_3', 'f_4')
ORDER BY routine_name, parameter_name;

以上将产生:

routine_name | data_type | parameter_name | data_type | proretset
-------------+-----------+----------------+-----------+----------
f_1          | record    | v1             | integer   | f
f_1          | record    | v2             | integer   | f
f_2          | record    | v1             | integer   | t
f_2          | record    | v2             | integer   | t
f_3          | record    | v1             | integer   | f
f_3          | record    | v2             | integer   | f
f_3          | record    | v3             | integer   | f
f_4          | record    | v1             | integer   | t
f_4          | record    | v2             | integer   | t
f_4          | record    | v3             | integer   | t

INFORMATION_SCHEMA.COLUMNS 模拟

对于它的价值,如果有人需要这个疯狂的东西,这是我想出的漂亮查询来模拟 SQL Server 的INFORMATION_SCHEMA.COLUMNS返回表值函数列的漂亮实现(这是我们在jOOQ 的代码中支持表值函数时真正需要的发电机):

SELECT 
  p.proname AS TABLE_NAME,
  columns.proargname AS COLUMN_NAME, 
  ROW_NUMBER() OVER(PARTITION BY p.oid ORDER BY o.ordinal) AS ORDINAL_POSITION,
  format_type(t.oid, t.typtypmod) AS DATA_TYPE,
  information_schema._pg_char_max_length(t.oid, t.typtypmod) AS CHARACTER_MAXIMUM_LENGTH,
  information_schema._pg_numeric_precision(t.oid, t.typtypmod) AS NUMERIC_PRECISION,
  information_schema._pg_numeric_scale(t.oid,t.typtypmod) AS NUMERIC_SCALE,
  not(t.typnotnull) AS IS_NULLABLE
FROM pg_proc p,
LATERAL generate_series(1, array_length(p.proargmodes, 1)) o(ordinal),
LATERAL (
  SELECT 
    p.proargnames[o.ordinal], 
    p.proargmodes[o.ordinal], 
    p.proallargtypes[o.ordinal]
) columns(proargname, proargmode, proargtype),
LATERAL (
  SELECT pg_type.oid oid, pg_type.* 
  FROM pg_type 
  WHERE pg_type.oid = columns.proargtype
) t
WHERE p.proretset
AND proargmode = 't'
AND p.proname LIKE 'f%';

以上很好地返回(列名缩短为 SO):

table_name | column_name  | ordinal | data_type | length | precision | scale | nullable
f_2        | v2           |       1 | integer   |        |        32 |     0 | t
f_4        | v2           |       1 | integer   |        |        32 |     0 | t
f_4        | v3           |       2 | integer   |        |        32 |     0 | t
于 2014-07-01T14:41:22.277 回答
2

RETURNS TABLE()实际上与OUT参数组合相同,RETURNS SETOF ... 如果没有附加SETOF关键字,带OUT参数的函数总是返回单行,而带参数的函数RETURNS TABLE()可以返回0-n 行

由于它的编写方式,您的示例恰好没有表现出差异。

这反映在proretset系统目录的标志中pg_proc手册:

函数返回一个集合(即指定数据类型的多个值)

信息模式视图information_schema.routines在这方面并不是特别有用。这是一种淡化的标准化妥协,以独立于平台的形式呈现信息,几乎不适合描述 Postgres 中的细节。

函数签名

手册:

如果两个函数具有相同的名称和输入参数类型,则它们被视为相同 ,忽略任何OUT参数

其中“输入”参数类型包括ININOUT参数。

这两个函数在摆弄函数定义时很有帮助。手册:

pg_get_function_arguments(func_oid) ... get argument list of function's definition (with default values)
pg_get_function_identity_arguments(func_oid) ... get argument list to identify a function (without default values)

这些相关答案中的更多内容:

于 2014-07-01T14:46:52.863 回答