考虑这两个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
中发现它?
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)
要删除函数,必须传递其输入(IN
和INOUT
(参数数据类型。然后我猜函数名称及其输入参数数据类型确实形成了它的签名。要更改返回的数据类型,必须首先将其删除并重新创建。
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
RETURNS TABLE()
与OUT
参数与RETURNS SETOF ...
的组合实际上相同如果没有额外的SETOF
关键字,具有OUT
参数的函数总是返回单行,而具有RETURNS TABLE()
的函数可以返回0-n行。
你的例子只是碰巧没有表现出差异,因为它是如何写的。
这反映在系统目录CCD_ 24的标志CCD_。手册:
函数返回一组(即指定数据类型的多个值(
信息模式视图information_schema.routines
在这方面不是特别有帮助。这是一种淡化的标准化折衷,以独立于平台的形式呈现信息,很难在Postgres中描述细节。
函数签名
手册:
如果两个函数具有相同的名称,并且输入参数类型,忽略任何
OUT
参数
其中";输入";参数类型包括CCD_ 27和CCD_。
这两个函数在处理函数定义时非常有用。手册:
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)
更多相关答案:
- 如何获取函数参数列表(这样我就可以删除函数(
- DROP FUNCTION而不知道参数的数量/类型