有没有办法在 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
。
但是,从 integer
到 smallint
的转换是 "only" 一个 赋值转换 。这就是旅程的终点:
No function matches the given name and argument types.
这些相关答案中更详细的解释:
PostgreSQL ERROR: function to_tsvector(character varying, unknown) does not exist
Generate series of dates - using date type as input
脏修复
你可以通过"upgrading"从integer
到smallint
的转换来解决这个问题隐式转换:
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)
我和我的用户在 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 typebigint
if its value fits in typebigint
(64 bits); otherwise it is taken to be typenumeric
. Constants that contain decimal points and/or exponents are always initially presumed to be typenumeric
.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
。
但是,从 integer
到 smallint
的转换是 "only" 一个 赋值转换 。这就是旅程的终点:
No function matches the given name and argument types.
这些相关答案中更详细的解释:
PostgreSQL ERROR: function to_tsvector(character varying, unknown) does not exist
Generate series of dates - using date type as input
脏修复
你可以通过"upgrading"从integer
到smallint
的转换来解决这个问题隐式转换:
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)