PostgreSQL与字符串和网络功能的行为不一致

时间:2016-02-22 15:08:03

标签: postgresql

PostgreSQL的inet函数通常接受正确的inet形式的字符串:

mydb=# select network('10.1.2.3/24');
   network
-------------
 10.1.2.0/24
(1 row)

但是,如果使用字符串连接来构造字符串,则相同的函数将失败:

mydb=# select network('10.1.2.3' || '/24');
ERROR:  function network(text) does not exist
LINE 1: select network('10.1.2.3' || '/24');
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

即使这两个字符串看起来是等价的:

mydb=# select '10.1.2.3/24';
  ?column?
-------------
 10.1.2.3/24
(1 row)

mydb=# select '10.1.2.3' || '/24';
  ?column?
-------------
 10.1.2.3/24
(1 row)

您可以通过将连接字符串转换为inet来解决此问题:

mydb=# select network(('10.1.2.3' || '/24')::inet);
   network
-------------
 10.1.2.0/24
(1 row)

任何人都可以通过某种方式来描述这种行为,这种方式可以帮助我理解为什是否存在不会影响连接字符串的整个字符串的隐式转换?

2 个答案:

答案 0 :(得分:2)

严格地说,PostgreSQL中引用的文字(例如'10.1.2.3/24')不是字符串,它是由解析器确定的某种类型的文字。

您可以使用SQL标准前缀表示法(inet '10.1.2.3/24')或Postgres后缀表示法('10.1.2.3/24'::inet)告诉解析器它的类型。这些不是类型转换,它们是特定类型的输入规范。如果您没有指定类型,解析器将尝试根据上下文猜测其类型,并回退到伪类型unknown

当您运行network('10.1.2.3/24')时,文字推断为inet类型,因为只有一个名为network的函数,这就是它所期望的类型。该值永远不会是text类型,因此不需要强制转换。

但是当您编写network('10.1.2.3' || '/24')时,需要先解析||运算符;要做到这一点,Postgres确定(正确)文字'10.1.2.3''/24'应被视为类型text,连接它们的结果也是text。然后,它会查找名为network()的函数,该函数采用text参数但未找到一个;它没有自动强制转换配置textinet,因此它失败并出现类型检查错误。

您可以通过创建一个名为network的新函数来实际看到这一点,该函数带有float参数:

create function network(float) returns text language sql as $$ select 'test'::text; $$

在范围内使用此函数运行select network('10.1.2.3/24')会出现错误“无法选择最佳候选函数”,因为Postgres不知道inetfloat是否为类型你打算使用你的文字'10.1.2.3/24'。 (如果你创建一个函数network(text),它将优先于两者;不是因为文字是文本类型,只是因为它被认为是“更好的候选者”。)

相反,如果指定Select network(text '10.1.2.3/24'),则会出现类型错误,而不是隐式转换。

答案 1 :(得分:1)

在应用inet函数之前,network似乎存在隐式类型转换。当您通过连接显式创建text对象时,这不起作用。您应该添加一个明确的inet类型转换:

db=# select network(('10.1.2.3'||'/24')::inet);
   network   
-------------
 10.1.2.0/24
(1 row)