关于postgresql:有没有办法在Postgres中禁用函数重载

Is there a way to disable function overloading in Postgres

我的用户和我不使用PL / pgSQL中的函数重载。 我们每个(模式,名称)元组总是有一个函数。 因此,我们只想按名称删除函数,更改其签名而不必先删除它等。例如,考虑以下函数:

1
2
3
4
5
6
7
8
9
CREATE OR REPLACE FUNCTION myfunc(day_number SMALLINT)
RETURNS TABLE(a INT)
AS
$BODY$
BEGIN
  RETURN QUERY (SELECT 1 AS a);
END;
$BODY$
LANGUAGE plpgsql;

为了节省时间,我们想按如下方式调用它,而不用::SMALLINT限定1,因为只有一个名为myfunc的函数,它只有一个名为day_number的参数:

1
SELECT * FROM myfunc(day_number := 1)

没有歧义,值1与SMALLINT类型一致,但PostgreSQL抱怨:

1
SELECT * FROM myfunc(day_number := 1);
1
2
3
4
5
ERROR:  FUNCTION myfunc(day_number := INTEGER) does NOT exist
LINE 12: SELECT * FROM myfunc(day_number := 1);
                       ^
HINT:  No FUNCTION matches the given name AND argument types.
You might need TO ADD explicit TYPE casts.

当我们从Python调用这些函数时,我们使用一个包装器来查找函数的签名并使用类型限定参数。 这种方法有效,但似乎有可能改进。

有没有办法完全关闭功能重载?


欧文发了正确答复。我的下一个回复与禁用重载的可能性有关。

无法禁用重载 - 这是PostgreSQL函数API系统的基本功能 - 无法禁用。我们知道有一些副作用,比如强大的功能特征刚性 - 但是当在视图,表定义中使用函数时,它可以防止一些令人不快的副作用。所以你不能禁用它。

您只需检查您是否有重载功能:

1
2
3
4
5
6
7
8
postgres=# SELECT COUNT(*), proname
               FROM pg_proc
              WHERE pronamespace <> 11
              GROUP BY proname
              HAVING COUNT(*) > 1;
 COUNT | proname
-------+---------
(0 ROWS)


这实际上不是函数重载的问题(不可能"关闭")。这是功能类型解析的问题。 (当然,没有重载函数,该算法可以更宽松。)

所有这些都可行:

1
2
3
4
5
SELECT * FROM myfunc(day_number := '1');
SELECT * FROM myfunc('1');               -- note the quotes

SELECT * FROM myfunc(1::SMALLINT);
SELECT * FROM myfunc('1'::SMALLINT);

为什么?

最后两个是相当明显的,你已经在你的问题中提到了。
前两个更有趣,解释隐藏在功能类型解析中:

unknown literals are assumed to be convertible to anything for this purpose.

这应该是你的简单解决方案:使用字符串文字。

SQL标准中定义的无类型文字'1'(带引号)或"字符串文字"在本质上与类型文字(或常量)不同。

数字常量1(不带引号)会立即转换为数字类型。手册:

A numeric constant that contains neither a decimal point nor an
exponent is initially presumed to be type integer if its value fits in
type integer (32 bits); otherwise it is presumed to be type bigint if
its value fits in type bigint (64 bits); otherwise it is taken to be
type numeric. Constants that contain decimal points and/or exponents
are always initially presumed to be type numeric.

The initially assigned data type of a numeric constant is just a
starting point for the type resolution algorithms. In most cases the
constant will be automatically coerced to the most appropriate type
depending on context. When necessary, you can force a numeric value to
be interpreted as a specific data type by casting it.

大胆强调我的。

函数调用(day_number := 1)中的赋值是一种特殊情况,此时day_number的数据类型是未知的。 Postgres无法从此分配派生数据类型,默认为integer

因此,Postgres首先寻找一个integer的函数。然后对于只接受integer的隐式转换的函数,换句话说:

1
2
3
4
SELECT casttarget::regtype
FROM   pg_cast
WHERE  castsource = 'int'::regtype
AND    castcontext = 'i';

所有这些都将被发现 - 如果有多个功能则会发生冲突。那将是函数重载,你会得到一个不同的错误信息。有两个这样的候选函数:

1
SELECT * FROM myfunc(1);
1
ERROR:  FUNCTION myfunc(INTEGER) IS NOT UNIQUE

请注意消息中的"整数":数字常量已转换为integer

但是,从integersmallint的强制转换是"仅"分配转换。这就是旅程结束的地方:

1
No FUNCTION matches the given name AND argument types.

SQL小提琴。

这些相关答案中有更详细的解释:

  • PostgreSQL错误:函数to_tsvector(字符变化,未知)不存在

  • 生成一系列日期 - 使用日期类型作为输入

脏修复

您可以通过将演员从integer"升级"到smallint到隐式演员来解决这个问题:

1
2
3
4
UPDATE pg_cast
SET    castcontext = 'i'
WHERE  castsource = 'int'::regtype
AND    casttarget  = 'int2'::regtype;

但我强烈反对篡改默认的投射系统。如果您确切知道自己在做什么,请考虑这一点。您可以在Postgres列表中找到相关的讨论。它可以有各种副作用,从功能类型分辨率开始,但不是在那里结束。

在旁边

功能类型分辨率完全独立于使用的语言。 SQL函数将与PL / perl或PL / pgSQL或"内部"函数竞争相同。功能签名至关重要。内置函数仅排在第一位,因为pg_catalog在默认search_path中排在第一位。


有很多内置函数被重载,所以如果关闭函数重载就不行。