10.2. 操作符
被一個操作符表達式引用的特定操作符由下列過程決定。注意這個過程會被所涉及的操作符的優先級間接地影響,因為這将決定哪些子表達式被用作哪個操作符的輸入。詳見
第 4.1.6 節。
操作符類型決定
- 從系統目錄
中選出要考慮的操作符。如果使用了一個不帶模式限定的操作符 名(常見的情況),那麼操作符被認為是那些在目前搜尋路徑中可見并有比對的名字和參數個數的操作符(參見 第 5.8.3 節 )。如果給出一個被限定的操作符名,那麼隻考慮指定模式中的操作符。pg_operator
- 如果搜尋路徑找到了多個有相同參數類型的操作符,那麼隻考慮最早出現在路徑中的那一個。 但是不同參數類型的操作符将被平等看待,而不管它們在路徑中的位置如何。
- 查找一個正好接受輸入參數類型的操作符。如果找到一個(在一組被考慮的操作符中,可能隻存在一個正好比對的),則使用之。
- 如果一個二進制操作符調用中的一個參數是
類型,則在本次檢查中假設它與另一個參數類型相同。 對于涉及兩個unknown
輸入的調用或者帶有一個unknown
輸入的一進制操作符,在這一步将永遠找不到一個比對。unknown
- 如果一個二進制操作符調用的其中一個參數是
類型 而另一個是一種域類型,下一次檢查會看看是否有一個操作符正好在兩邊都 接受該域的基類型,如果有就使用它。unknown
- 如果一個二進制操作符調用中的一個參數是
- 尋找最優比對。
- 抛棄那些輸入類型不比對并且也不能被轉換成比對的候選操作符。
文字被假定為可以為這個目的被轉換為 任何東西。如果隻剩下一個候選操作符,則使用之,否則繼續下一 步。unknown
- 如果任何輸入參數是一種域類型,對所有後續步驟都把它當做是該 域的基類型。這確定在做有歧義的操作符解析時,域的舉止像它們 的基類型。
- 周遊所有候選操作符,保留那些在輸入類型上的比對最準确的。如果沒有一個操作符能準确比對,則保留所有候選。如果隻剩下一個候選操作符,則使用之,否則繼續下一步。
- 周遊所有候選操作符,保留那些在最多個需要類型轉換的位置上接受首選類型(屬于輸入資料類型的類型分類)的操作符。如果沒有接受首選類型的操作符,則保留所有候選。如果隻剩下一個候選操作符,則使用之, 否則繼續下一步。
- 如果有任何輸入參數是
類型,檢查被剩餘候選操作符在那些參數位置上接受的類型分類。 在每一個位置,如果任何候選接受該分類,則選擇unknown
分類(這種對字元串的偏愛是合适的, 因為未知類型的文本确實像字元串)。否則,如果所有剩下的候選操作符都接受相同的類型 分類,則選擇該分類;否則抛出一個錯誤(因為在沒有更多線索的條件下無法作出正确 的推斷)。現在抛棄不接受標明的類型分類的候選操作符。然後,如果任意候選操作符接受那個分類中的首選類型, 則抛棄那些在該參數位置接受非首選類型的候選操作符。如果沒有候選操作符能通過這些測試則保留全部候選者。如果隻剩下一個候選者,則使用之;否則繼續下一步。string
- 如果既有
參數也有已知類型的參數,并且所有已知類型參數具有相同的類型,則假定該unknown
參數也是那種類型的,并且檢查哪些候選操作符可以在該unknown
參數的位置上接受那個類型。如果正好有一個候選者通過了這個測試,則使用之;否則失敗。unknown
- 抛棄那些輸入類型不比對并且也不能被轉換成比對的候選操作符。
下面是一些例子。
例 10.1. 階乘操作符類型決定
在标準目錄中隻有一個被定義的階乘操作符(字尾
!
),它接受一個類型為
bigint
的參數。在下面這個查詢表達式中,掃描器會為該參數配置設定一個初始類型
integer
:
SELECT 40 ! AS "40 factorial";
40 factorial
--------------------------------------------------
815915283247897734345611269596115894272000000000
(1 row)
是以,解析器在操作數上做了一個類型轉換,該查詢等價于:
SELECT CAST(40 AS bigint) ! AS "40 factorial";
例 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
類型,它是在數字分類中的首選類型。 是以,PostgreSQL将在遇到一個
unknown
輸入時使用它:
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.20 節)中沒有一個被認為是首選的,解析器不能以此為基礎來解決歧義。不過,
步驟 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
操作符。如果 到達最佳比對規則,它們會積極地排斥域類型上的操作符。如果它 們沒有,這樣一個操作符将建立太多歧義操作符失敗,因為轉換規 則總是認為一個域可以和它的基類型互相轉換,并且是以該域操作 符在所有與該基類型上的一個類似命名的操作符相同的情況中都被 認為可用。
本文轉自PostgreSQL中文社群,原文連結: