PostgreSQL 操作符

2021-08-27 11:39 更新

被一個(gè)操作符表達(dá)式引用的特定操作符由下列過(guò)程決定。注意這個(gè)過(guò)程會(huì)被所涉及的操作符的優(yōu)先級(jí)間接地影響,因?yàn)檫@將決定哪些子表達(dá)式被用作哪個(gè)操作符的輸入。詳見(jiàn)第 4.1.6 節(jié)。

操作符類型決定

  1. 從系統(tǒng)目錄pg_operator中選出要考慮的操作符。如果使用了一個(gè)不帶模式限定的操作符 名(常見(jiàn)的情況),那么操作符被認(rèn)為是那些在當(dāng)前搜索路徑中可見(jiàn)并有匹配的名字和參數(shù)個(gè)數(shù)的操作符(參見(jiàn)第 5.9.3 節(jié))。如果給出一個(gè)被限定的操作符名,那么只考慮指定模式中的操作符。

    1. 如果搜索路徑找到了多個(gè)有相同參數(shù)類型的操作符,那么只考慮最早出現(xiàn)在路徑中的那一個(gè)。 但是不同參數(shù)類型的操作符將被平等看待,而不管它們?cè)诼窂街械奈恢萌绾巍?/p>

  2. 查找一個(gè)正好接受輸入?yún)?shù)類型的操作符。如果找到一個(gè)(在一組被考慮的操作符中,可能只存在一個(gè)正好匹配的),則使用之。在通過(guò)限定名稱(非典型)調(diào)用在一個(gè)允許不可信用戶創(chuàng)建對(duì)象的方案中找到的任意操作符時(shí),精確匹配的缺失會(huì)導(dǎo)致安全性危害 [8] 。在這樣的情況下,應(yīng)該造型參數(shù)以便強(qiáng)制一次精確匹配。

    1. 如果一個(gè)二元操作符調(diào)用中的一個(gè)參數(shù)是unknown類型,則在本次檢查中假設(shè)它與另一個(gè)參數(shù)類型相同。 對(duì)于涉及兩個(gè)unknown輸入的調(diào)用或者帶有一個(gè)unknown輸入的一元操作符,在這一步將永遠(yuǎn)找不到一個(gè)匹配。

    2. 如果一個(gè)二元操作符調(diào)用的其中一個(gè)參數(shù)是unknown類型 而另一個(gè)是一種域類型,下一次檢查會(huì)看看是否有一個(gè)操作符正好在兩邊都 接受該域的基類型,如果有就使用它。

  3. 尋找最優(yōu)匹配。

    1. 拋棄那些輸入類型不匹配并且也不能被轉(zhuǎn)換成匹配的候選操作符。 unknown文字被假定為可以為這個(gè)目的被轉(zhuǎn)換為 任何東西。如果只剩下一個(gè)候選操作符,則使用之,否則繼續(xù)下一 步。

    2. 如果任何輸入?yún)?shù)是一種域類型,對(duì)所有后續(xù)步驟都把它當(dāng)做是該 域的基類型。這確保在做有歧義的操作符解析時(shí),域的舉止像它們 的基類型。

    3. 遍歷所有候選操作符,保留那些在輸入類型上的匹配最準(zhǔn)確的。如果沒(méi)有一個(gè)操作符能準(zhǔn)確匹配,則保留所有候選。如果只剩下一個(gè)候選操作符,則使用之,否則繼續(xù)下一步。

    4. 遍歷所有候選操作符,保留那些在最多個(gè)需要類型轉(zhuǎn)換的位置上接受首選類型(屬于輸入數(shù)據(jù)類型的類型分類)的操作符。如果沒(méi)有接受首選類型的操作符,則保留所有候選。如果只剩下一個(gè)候選操作符,則使用之, 否則繼續(xù)下一步。

    5. 如果有任何輸入?yún)?shù)是unknown類型,檢查被剩余候選操作符在那些參數(shù)位置上接受的類型分類。 在每一個(gè)位置,如果任何候選接受該分類,則選擇string分類(這種對(duì)字符串的偏愛(ài)是合適的, 因?yàn)槲粗愋偷奈谋敬_實(shí)像字符串)。否則,如果所有剩下的候選操作符都接受相同的類型 分類,則選擇該分類;否則拋出一個(gè)錯(cuò)誤(因?yàn)樵跊](méi)有更多線索的條件下無(wú)法作出正確 的推斷)?,F(xiàn)在拋棄不接受選定的類型分類的候選操作符。然后,如果任意候選操作符接受那個(gè)分類中的首選類型, 則拋棄那些在該參數(shù)位置接受非首選類型的候選操作符。如果沒(méi)有候選操作符能通過(guò)這些測(cè)試則保留全部候選者。如果只剩下一個(gè)候選者,則使用之;否則繼續(xù)下一步。

    6. 如果既有unknown參數(shù)也有已知類型的參數(shù),并且所有已知類型參數(shù)具有相同的類型,則假定該unknown參數(shù)也是那種類型的,并且檢查哪些候選操作符可以在該unknown參數(shù)的位置上接受那個(gè)類型。如果正好有一個(gè)候選者通過(guò)了這個(gè)測(cè)試,則使用之;否則失敗。

下面是一些例子。

例 10.1. 平方根運(yùn)算符類型解析

在標(biāo)準(zhǔn)目錄中只有一個(gè)被定義的平方根操作符(前綴|/),它接受一個(gè)類型為double precision的參數(shù)。在下面這個(gè)查詢表達(dá)式中,掃描器會(huì)為該參數(shù)分配一個(gè)初始類型integer

SELECT |/ 40 AS "square root of 40";
 square root of 40
-------------------
 6.324555320336759
(1 row)

因此,解析器在操作數(shù)上做了一個(gè)類型轉(zhuǎn)換,該查詢等價(jià)于:

SELECT |/ CAST(40 AS double precision) AS "square root of 40";

例 10.2. 字符串連接操作符類型決定

一個(gè)類字符串的語(yǔ)法被用來(lái)處理字符串類型和處理復(fù)雜的擴(kuò)展類型。未指定類型的字符串與可能的候選操作符匹配。

一個(gè)未指定參數(shù)的例子:

SELECT text 'abc' || 'def' AS "text and unknown";

 text and unknown
------------------
 abcdef
(1 row)

在這種情況下,解析器查看是否有一個(gè)操作符的兩個(gè)參數(shù)都使用text。既然有,那么它假設(shè)第二個(gè)參數(shù)應(yīng)被解釋為text類型。

下面是兩個(gè)未指定類型的值的連接:

SELECT 'abc' || 'def' AS "unspecified";

 unspecified
-------------
 abcdef
(1 row)

在這種情況下,沒(méi)有對(duì)于使用哪種類型的初始提示,因?yàn)樵诓樵冎袥](méi)有指定類型。 因此,解析器查找所有的候選操作符并找到候選者同時(shí)接受字符串分類和位串分類的輸入。 因?yàn)樽址诸愒诳捎脮r(shí)是首選的,該分類會(huì)被選中,并且接下來(lái)字符串的首選類型(text)會(huì)被用作解決未知類型文字的指定類型。


例 10.3. 絕對(duì)值與否定操作符類型決定

PostgreSQL操作符目錄中有幾個(gè)對(duì)于前綴操作符@的條目, 這些都現(xiàn)實(shí)了針對(duì)各種數(shù)字?jǐn)?shù)據(jù)類型的絕對(duì)值操作。其中之一用于float8類型,它是在數(shù)字分類中的首選類型。 因此,PostgreSQL將在遇到一個(gè)unknown輸入時(shí)使用它:

SELECT @ '-4.5' AS "abs"; abs ----- 4.5 (1 row)

在這里,系統(tǒng)在應(yīng)用所選操作符之前已經(jīng)隱式地解決了將未知類型文字作為float8類型。 我們可以驗(yàn)證我們使用的是float8而不是別的類型:

SELECT @ '-4.5e500' AS "abs";

ERROR:  "-4.5e500" is out of range for type double precision

另一方面,前綴符~(按位取反)只為整數(shù)數(shù)據(jù)類型定義,而沒(méi)有為float8定義。因此,如果我們嘗試一個(gè)與使用~類似的情況,我們會(huì)得到:

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.

這是因?yàn)橄到y(tǒng)不能決定在幾個(gè)可能的~符號(hào)中應(yīng)該選擇哪一個(gè)。我們可以用一個(gè)顯式造型來(lái)幫助它:

SELECT ~ CAST('20' AS int8) AS "negation";

 negation
----------
      -21
(1 row)

例 10.4. 數(shù)組包含操作符類型決定

這里是另一個(gè)決定帶有一個(gè)已知和一個(gè)未知輸入的操作符的例子:

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

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

PostgreSQL操作符目錄有一些條目用于中綴操作符<@,但是僅有的兩個(gè)可以在左手邊接受一個(gè)整數(shù)數(shù)組的是數(shù)組包含(anyarray <@ anyarray)和范圍包含(anyelement <@ anyrange)。因?yàn)檫@些多態(tài)偽類型(見(jiàn)第 8.21 節(jié))中沒(méi)有一個(gè)被認(rèn)為是首選的,解析器不能以此為基礎(chǔ)來(lái)解決歧義。不過(guò),本文中操作符類型決定中第3點(diǎn)中的 f 告訴它假定位置類型的文字和其他輸入的類型相同,即整數(shù)數(shù)組。現(xiàn)在這兩個(gè)操作符中只有一個(gè)可以匹配,因此數(shù)組包含被選擇(如果選擇范圍包含,我們將得到一個(gè)錯(cuò)誤,因?yàn)樵撟址疀](méi)有成為一個(gè)范圍文字的正確格式)。


例 10.5. 域類型上的自定義操作符

用戶有時(shí)會(huì)嘗試聲明只適用于一種域類型的操作符。這是可能的, 但是遠(yuǎn)非它看起來(lái)那么有用,因?yàn)椴僮鞣馕鲆?guī)則被設(shè)計(jì)為選擇 適用于域的基類型的操作符??紤]這個(gè)例子:

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';

這個(gè)查詢將不會(huì)使用自定義操作符。解析器將首先看看是否有一個(gè) mytext = mytext操作符( 本文中操作符類型決定中第 2點(diǎn)中 a),當(dāng)然這里沒(méi)有; 然后它將會(huì)考慮該域的基類型text,并且看看是否有一 個(gè)text = text操作符( 本文中操作符類型決定中第2點(diǎn)中.b),這里也沒(méi)有;因 此它會(huì)把unknown-類型文字解析為text 并使用 text = text操作符。 讓自定義操作符能被使用的唯一方法是顯式地轉(zhuǎn)換改文字:

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

這樣根據(jù)準(zhǔn)確匹配規(guī)則會(huì)立即找到 mytext = text操作符。如果 到達(dá)最佳匹配規(guī)則,它們會(huì)積極地排斥域類型上的操作符。如果它 們沒(méi)有,這樣一個(gè)操作符將創(chuàng)建太多歧義操作符失敗,因?yàn)檗D(zhuǎn)換規(guī) 則總是認(rèn)為一個(gè)域可以和它的基類型相互轉(zhuǎn)換,并且因此該域操作 符在所有與該基類型上的一個(gè)類似命名的操作符相同的情況中都被 認(rèn)為可用。




[8] 對(duì)非方案限定的名稱,不會(huì)出現(xiàn)這種危害,因?yàn)榘试S不可信用戶創(chuàng)建對(duì)象的方案的搜索路徑不是一種安全的方案使用模式。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)