有没有办法在Postgres中禁用函数重载

时间:2015-02-24 14:40:48

标签: postgresql types casting plpgsql postgresql-9.3

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

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的参数:

SELECT * FROM myfunc(day_number := 1)

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

SELECT * FROM myfunc(day_number := 1);
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调用这些函数时,我们使用一个包装器来查找函数的签名并使用类型限定参数。这种方法有效,但似乎有可能改进。

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

3 个答案:

答案 0 :(得分:6)

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

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

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

postgres=# select count(*), proname 
               from pg_proc 
              where pronamespace <> 11 
              group by proname 
              having count(*) > 1;
 count | proname 
-------+---------
(0 rows)

答案 1 :(得分:4)

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

所有这些都可行:

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

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

为什么?

最后两个相当明显,你已经在你的问题中提到了这一点 前两个更有趣,解释隐藏在Function Type Resolution

  为了这个目的,假定未知的文字可以转换成任何东西。

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

SQL标准中定义的无类型文字('1',带引号)或“字符串文字”在性质上与任何其他类型的文字(或常量)不同。

数字常量(1,不带引号)会立即转换为数字类型 Per documentation:

  

既不包含小数点也不包含小数点的数字常量   如果指数的值适合,则指数最初被假定为类型为整数   type integer(32位);否则,如果是,则假定为bigint类型   它的值适合类型bigint(64位);否则它被认为是   输入numeric。包含小数点和/或指数的常量   最初总是假定为numeric类型。

     

数字常量的初始分配数据类型只是a   类型分辨率算法的起点。在大多数情况下   常量将自动强制转换为最合适的类型   取决于具体情况。必要时,可以强制使用数值   通过强制转换将其解释为特定的数据类型。

大胆强调我的。

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

因此,Postgres会寻找一个首先采用integer 的函数。然后,对于仅使用隐式投射类型的函数远离integer,换句话说:

SELECT casttarget::regtype
FROM   pg_cast
WHERE  castsource = 'int'::regtype
AND    castcontext = 'i';

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

SELECT * FROM myfunc(1);
ERROR:  function myfunc(integer) is not unique

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

但是,从integersmallint的演员阵营“仅”是分配广告。这就是旅程结束的地方:

No function matches the given name and argument types.

SQL Fiddle.

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

脏修复

可以 通过将演员阵容从integer升级到smallint再到隐式演员来解决此问题:

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

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

除了

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

答案 2 :(得分:0)

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