Postgresql 中文操作指南
10.2. Operators #
由运算符表达式引用的特定运算符是使用以下过程确定的。请注意,此过程间接受到所涉及运算符优先级的影响,因为这将确定哪些子表达式被视为哪些运算符的输入。有关更多信息,请参阅 Section 4.1.6。
Operator Type Resolution
后面有一些示例。
Example 10.1. Square Root Operator Type Resolution
标准目录中仅定义了一个平方根运算符(前缀 |/),它接收类型 double precision 的参数。扫描器会将初始类型 integer 指定给此查询表达式中的参数:
SELECT |/ 40 AS "square root of 40";
square root of 40
-------------------
6.324555320336759
(1 row)
因此,解析器对操作数执行类型转换,查询等价于:
SELECT |/ CAST(40 AS double precision) AS "square root of 40";
Example 10.2. String Concatenation Operator Type Resolution
字符串类似语法用于处理字符串类型和复杂扩展类型。未指定类型的字符串与可能的候选运算符匹配。
一个未指定参数的示例:
SELECT text 'abc' || 'def' AS "text and unknown";
text and unknown
------------------
abcdef
(1 row)
在此情况下,解析器会查看有没有运算符将 text 作为两个参数。由于存在,它假定应将第二个参数解释为类型 text。
以下是两个未指定类型的值的连接:
SELECT 'abc' || 'def' AS "unspecified";
unspecified
-------------
abcdef
(1 row)
在此情况下,没有任何类型的初始提示可用,因为查询中未指定任何类型。因此,解析器会查找所有候选运算符,并找出有接受字符串类别和位字符串类别输入的候选运算符。由于有字符串类别可用时会优先选择字符串类别,因此会选择该类别,然后使用字符串的首选类型 text 作为解析未知类型文字的具体类型。
Example 10.3. Absolute-Value and Negation Operator Type Resolution
PostgreSQL 运算符目录中有一个前缀运算符 @ 的多个条目,所有这些条目都为各种数字数据类型实现绝对值运算。其中一个条目是类型 float8 的,该类型是数字类别中的首选类型。因此,当处理 unknown 输入时,PostgreSQL 会使用该条目:
SELECT @ '-4.5' AS "abs";
abs
-----
4.5
(1 row)
在此,系统在应用所选运算符之前已将未知类型文字隐式解析为类型 float8。我们可以验证使用了 float8 而不是其他类型:
SELECT @ '-4.5e500' AS "abs";
ERROR: "-4.5e500" is out of range for type double precision
另一方面,前缀运算符 ~(按位取反)仅定义为整数数据类型,不适用于 float8。因此,如果我们尝试使用 ~ 进行类似的操作,我们得到:
SELECT ~ '20' AS "negation";
ERROR: operator is not unique: ~ "unknown"
HINT: Could not choose a best candidate operator. You might need to add
explicit type casts.
之所以会发生这种情况,是因为系统无法确定应优先选用哪几个可能的 ~ 运算符。我们可以通过显式强制转换来帮助该系统:
SELECT ~ CAST('20' AS int8) AS "negation";
negation
----------
-21
(1 row)
Example 10.4. Array Inclusion Operator Type Resolution
以下是解析具有一个已知输入和一个未知输入的运算符的另一个示例:
SELECT array[1,2] <@ '{1,2,3}' as "is subset";
is subset
-----------
t
(1 row)
PostgreSQL 操作符目录中有几个 infix 操作符 <@ 的条目,但只有两个可能在左侧接受一个整数数组,即数组包含 (anyarray <@ anyarray) 和范围包含 (anyelement <@ anyrange)。由于这些多态伪类型(参见 Section 8.21)都不被认为是首选的,解析器无法在此基础上消除歧义。然而, Step 3.f 告诉它假设未知类型文本是与其他输入相同的类型,即整数数组。现在只有两个操作符中的一个可以匹配,因此选择了数组包含。(如果选择了范围包含,我们会得到一个错误,因为字符串没有成为范围文本的正确格式。)
Example 10.5. Custom Operator on a Domain Type
用户有时尝试仅仅对某个域类型声明运算符。这是可能的,但并不会像看起来那么有用,因为运算符解析规则旨在选择适用于该域的基本类型的运算符。作为示例,考虑
CREATE DOMAIN mytext AS text CHECK(...);
CREATE FUNCTION mytext_eq_text (mytext, text) RETURNS boolean AS ...;
CREATE OPERATOR = (procedure=mytext_eq_text, leftarg=mytext, rightarg=text);
CREATE TABLE mytable (val mytext);
SELECT * FROM mytable WHERE val = 'foo';
此查询将不会使用自定义操作符。解析器会首先查看是否有 mytext = mytext 操作符 ( Step 2.a),但没有;然后它将考虑域的基类型 text,并查看是否有 text = text 操作符 ( Step 2.b),是有;因此它将 unknown- 类型的文本解析为 text 并使用 text = text 操作符。让自定义操作符被使用的唯一方式是对文本进行显式转换:
SELECT * FROM mytable WHERE val = text 'foo';
以便根据精确匹配规则立即找到 mytext = text 运算符。如果达到最佳匹配规则,则会主动区分域类型运算符。如果不然,则此类运算符将创建过多的模棱两可的运算符失败问题,因为强制类型转换规则会始终考虑将域强制类型转换成其基类型或在其基类型强制类型转换,因此在所有类似于基类型运算符命名的情况下,域运算符都可用。
[9 ] 如果使用非架构限定的名称,则不会出现风险,因为包含允许不受信任的用户创建对象的架构的搜索路径并非 secure schema usage pattern 。