支援的版本:目前 (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 類別。(這種對字串的偏好是適當的,因為 unknown 型別的常值看起來像字串。)否則,如果所有剩餘的候選運算子都接受相同的型別類別,則選取該類別;否則失敗,因為如果沒有更多線索,則無法推斷出正確的選擇。現在捨棄不接受所選型別類別的候選運算子。此外,如果任何候選運算子在該類別中接受慣用型別,則捨棄接受該引數非慣用型別的候選運算子。如果沒有任何候選運算子通過這些測試,則保留所有候選運算子。如果只剩下一個候選運算子,則使用它;否則繼續下一步。

    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)

在這種情況下,由於查詢中沒有指定任何類型,因此沒有要使用哪種類型的初始提示。因此,剖析器會尋找所有候選運算子,並發現有候選運算子接受字串類別 (string-category) 和位元字串類別 (bit-string-category) 的輸入。由於在可用時首選字串類別,因此會選擇該類別,然後將字串的首選類型 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)。由於這些多型偽類型 (polymorphic pseudo-types) 中沒有一個被認為是首選,因此剖析器無法在此基礎上解決歧義。但是,步驟 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),如果有的話;因此它將 unknown 類型文字解析為 text 並使用 text = text 運算子。使用自訂運算子的唯一方法是顯式轉換文字

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

以便根據完全匹配規則立即找到 mytext = text 運算子。如果達到最佳匹配規則,它們會主動區分網域類型上的運算子。如果沒有,這樣的運算子會產生太多不明確的運算子失敗,因為轉換規則始終將網域視為可轉換為或從其基本類型轉換,因此網域運算子將被認為可在所有與基本類型上類似命名的運算子相同的情況下使用。




[9] 非架構限定名稱 (non-schema-qualified name) 不會產生危險,因為包含允許不受信任的使用者建立物件的架構的搜尋路徑不是 安全架構使用模式

提交更正

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