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

Is there a way to disable function overloading in Postgres

我和我的用户在 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 调用此类函数时,我们使用一个包装器来查找函数的签名并使用类型限定参数。这种方法有效,但似乎还有改进的潜力。

有没有办法完全关闭函数重载?

有很多内置函数被重载,所以如果您关闭函数重载,它就无法工作。

这实际上不是函数重载的直接问题("turn off" 是不可能的)。这是 函数类型解析 的问题。 (当然,如果没有重载函数,该算法可能会更宽松。)

所有这些都行得通:

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

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

为什么?

最后两个很明显,你已经在你的问题中提到了。
前两个比较有意思,解释都埋在了Function Type Resolution:

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

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

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

数字常量 1(不带引号)被转换为数字类型立即The manual:

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的数据类型为unknown。 Postgres 无法从此赋值派生数据类型,默认为 integer.

因此,Postgres 会寻找一个采用 integer first 的函数。然后对于从 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":数字常量已转换为 integer

但是,从 integersmallint 的转换是 "only" 一个 赋值转换 。这就是旅程的终点​​:

No function matches the given name and argument types.

SQL Fiddle.

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

  • PostgreSQL ERROR: function to_tsvector(character varying, unknown) does not exist

  • Generate series of dates - using date type as input

脏修复

可以通过"upgrading"从integersmallint的转换来解决这个问题隐式转换:

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

但我强烈反对篡改默认的施法系统。仅当您确切地知道自己在做什么时才考虑这一点。您将在 Postgres 列表中找到相关讨论。它可能有各种副作用,从函数类型解析开始,但不止于此。

放在一边

函数类型解析完全独立于使用的语言。 SQL 函数将与 PL/perl 或 PL/pgSQL 或 "internal" 函数竞争。函数签名是必不可少的。内置函数只排在第一位,因为 pg_catalog 默认排在第一位 search_path.

欧文发送了正确的回复。我的下一个回复是关于禁用重载的可能性。

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

您可以简单地检查您是否有重载函数:

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