2025年9月25日: PostgreSQL 18 釋出!
支援版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不再支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

10.2. 運算子 #

透過以下過程確定運算子表示式引用的特定運算子。請注意,此過程受涉及運算子的優先順序的間接影響,因為優先順序將決定哪些子表示式被視為哪些運算子的輸入。有關更多資訊,請參見第 4.1.6 節

運算子型別解析

  1. pg_operator 系統目錄中選擇要考慮的運算子。如果使用了非模式限定的運算子名稱(通常情況),則考慮的運算子是名稱和引數計數匹配且在當前搜尋路徑中可見的運算子(請參見第 5.10.3 節)。如果給出了模式限定的運算子名稱,則只考慮指定模式中的運算子。

    1. 如果在搜尋路徑中找到多個具有相同引數型別的運算子,則只考慮路徑中最早出現的那個。不同引數型別的運算子被平等對待,無論其在搜尋路徑中的位置如何。

  2. 檢查是否存在接受與輸入引數型別完全匹配的運算子。如果存在(考慮的運算子集中最多隻能有一個精確匹配),則使用它。缺少精確匹配在呼叫時(透過限定名稱[9](不典型),任何在允許不受信任使用者建立物件的模式中找到的運算子)會產生安全風險。在這種情況下,請強制轉換引數以強制執行精確匹配。

    1. 如果二元運算子呼叫中的一個引數是 unknown 型別,則在此檢查中假定它與另一個引數相同。涉及兩個 unknown 輸入的呼叫,或帶有 unknown 輸入的字首運算子,在此步驟中將永遠找不到匹配項。

    2. 如果二元運算子呼叫中的一個引數是 unknown 型別,而另一個引數是域型別,則接下來檢查是否存在一個運算子在兩側都精確接受域的基礎型別;如果存在,則使用它。

  3. 查詢最佳匹配。

    1. 丟棄候選運算子,這些運算子的輸入型別不匹配且無法透過(使用隱式轉換)進行轉換以匹配。為此,假定 unknown 字面量可以轉換為任何型別。如果只剩下一個候選項,則使用它;否則,繼續下一步。

    2. 如果任何輸入引數是域型別,則在後續所有步驟中將其視為域的基礎型別。這可確保域在歧義運算子解析方面像其基礎型別一樣。

    3. 遍歷所有候選項,並保留輸入型別匹配最多的候選項。如果沒有精確匹配,則保留所有候選項。如果只剩下一個候選項,則使用它;否則,繼續下一步。

    4. 遍歷所有候選項,並保留在需要進行型別轉換的位置上接受首選型別(輸入資料型別的型別類別)的候選項。如果沒有候選項接受首選型別,則保留所有候選項。如果只剩下一個候選項,則使用它;否則,繼續下一步。

    5. 如果任何輸入引數是 unknown,請檢查剩餘候選項在這些引數位置上接受的型別類別。在每個位置,如果任何候選項接受 string 類別,則選擇該類別。(偏好字串是合適的,因為未知型別的字面量看起來像字串。)否則,如果所有剩餘候選項接受相同的型別類別,則選擇該類別;否則失敗,因為無法在沒有更多線索的情況下推斷出正確的選擇。現在丟棄不接受所選型別類別的候選項。此外,如果任何候選項接受該類別中的首選型別,則丟棄接受非首選型別作為該引數的候選項。如果沒有候選項透過這些測試,則保留所有候選項。如果只剩下一個候選項,則使用它;否則,繼續下一步。

    6. 如果存在 unknown 和已知型別引數,並且所有已知型別引數具有相同的型別,則假定 unknown 引數也具有該型別,並檢查哪些候選項可以在 unknown 引數位置上接受該型別。如果恰好有一個候選項透過此測試,則使用它。否則,失敗。

以下是一些示例。

示例 10.1. 平方根運算子型別解析

標準目錄中只有一個平方根運算子(字首 |/),它接受 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";

示例 10.2. 字串連線運算子型別解析

使用類似字串的語法來處理字串型別和處理複雜擴充套件型別。未指定型別的字串會與可能的運算子候選項匹配。

一個具有一個未指定引數的示例

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 作為解析未知型別字面量的具體型別。


示例 10.3. 絕對值和求反運算子型別解析

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)

示例 10.4. 陣列包含運算子型別解析

這是另一種解析具有一個已知和一個未知輸入的運算子的示例。

SELECT array[1,2] <@ '{1,2,3}' as "is subset";

 is subset
-----------
 t
(1 row)

PostgreSQL 運算子目錄有幾個中綴運算子 <@ 的條目,但只有兩個可能接受左側的整數陣列:陣列包含(anyarray <@ anyarray)和範圍包含(anyelement <@ anyrange)。由於這些多型偽型別(參見第 8.21 節)都不是首選型別,因此解析器無法在此基礎上解析歧義。但是,步驟 3.f 告訴它假定未知型別的字面量與另一個輸入(即整數陣列)具有相同的型別。現在只有一個運算子可以匹配,因此選擇陣列包含。(如果選擇了範圍包含,則會收到錯誤,因為字串的格式不正確,無法作為範圍字面量。)


示例 10.5. 域型別上的自定義運算子

使用者有時會嘗試宣告僅應用於域型別的運算子。這是可能的,但不如看起來有用,因為運算子解析規則旨在選擇應用於域基礎型別的運算子。例如,考慮以下:

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 運算子(步驟 2.a),但不存在;然後它將考慮域的基礎型別 text,並檢查是否存在 text = text 運算子(步驟 2.b),存在;因此,它將未知型別的字面量解析為 text,並使用 text = text 運算子。要使用自定義運算子的唯一方法是顯式強制轉換字面量。

SELECT * FROM mytable WHERE val = text 'foo';

以便根據精確匹配規則立即找到 mytext = text 運算子。如果達到最佳匹配規則,則它們會主動歧視域型別上的運算子。如果它們不這樣做,這樣的運算子將導致過多的歧義運算子失敗,因為強制轉換規則始終將域視為可強制轉換為其基礎型別或從其基礎型別強制轉換,因此域運算子將在與基礎型別上同名運算子相同的情況下被考慮使用。




[9] 對於非模式限定名稱,不會出現危險,因為包含允許不受信任使用者建立物件的模式的搜尋路徑不是安全的模式使用模式

提交更正

如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用此表格報告文件問題。