演算式に参照される特定の演算子は、以下の手順を用いて決定されます。 関連する演算子の優先順位によりどの下位式をどの演算子の入力と見なすかが決定されますので、この手順はこの優先順位により間接的な影響を受けることに注意してください。 詳細は4.1.6を参照してください。
演算子における型の解決
pg_operator
システムカタログから、調査の対象とする演算子を選択します。
スキーマ修飾がされていない演算子名が使用される場合(通常の場合)、現行の検索パスで可視になっていて、同一の名前と引数の数を持つ演算子が調査対象であるとみなされます
(5.9.3を参照してください)。
修飾された演算子名が与えられている場合、指定されたスキーマの演算子のみが調査対象とみなされます。
検索パスで引数のデータ型が同じである複数の演算子を検出した場合、そのパスで最初に検出された演算子のみを調査対象とみなします。 引数のデータ型が異なる演算子は、検索パス内の位置に関係なく、同じように調べられます。
正確に入力引数型を受け付ける演算子があるかどうか検査します。 該当する演算子があれば(調査される演算子の集合内で正確に一致するものは1つしかあり得ません)、それを使用します。 正確に一致するものがない場合、信用できないユーザにオブジェクトの作成を許可しているスキーマで見つかる演算子を、(典型的なものではないですが)修飾された名前で [9] 呼び出す時にセキュリティの危険が発生します。 そのような状況では、強制的に正確に一致するように引数をキャストしてください。
二項演算子の1つの引数がunknown
型であった場合、この検査のもう片方の引数と同一の型であると仮定します。
2つのunknown
入力、もしくはunknown
入力を伴う前置演算子が呼び出された場合、この段階で対を見つけることはありません。
二項演算子の1つの引数がunknown
型であり、もう1つがドメイン型の場合、次に両側でドメインの基本型を厳密に受け付ける演算子があるかを確認します。
最もよく合うものを検索します。
演算子の候補のうち、入力値のデータ型が一致せず、また、(暗黙的な変換を使用して)一致するように変換できないものを破棄します。
unknown
リテラルは、上記の目的で何にでも変換可能とみなされます。
1つの候補しか残らない場合、それを使います。
それ以外の場合は次の段階に進みます。
入力引数のいずれかがドメイン型であれば、以降の段階すべてでドメインの基本型であるかのように扱います。 これにより、曖昧な演算子を解決するのを目的としてその基本型であるかのようにドメインが振る舞うことが確実になります。
全ての候補を検索し、入力型に最も正確に合うものを残します。 正確に合うものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
全ての候補を検索し、型変換が必要とされる所で(入力データ型カテゴリの)優先される型を受け付けるものを残します。 優先される型を受け付けるものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
入力引数でunknown
のものがあった場合、それらの残った候補に引数位置で受け入れられる型カテゴリを検査します。
各位置において、候補がstring
カテゴリを受け付ける場合は、そのカテゴリを選択します
(unknown 型のリテラルは文字列のようなものですので、この文字列への重み付けは適切です)。
そうでなければ、もし残った全ての候補が同じ型カテゴリを受け入れる場合はそのカテゴリを選択します。
そうでもなければ、さらに手掛かりがなければ正しい選択が演繹されることができませんので、失敗となります。
ここで、選択された型カテゴリを受け付けない演算子候補は破棄されます。
さらに、それらカテゴリ内の優先される型を受け付ける候補が1つでもある場合、その引数の優先されない型を受け付ける候補は破棄されます。
これらの検査をどれも通らなかったら全ての候補を残します。
1つの候補しか残らない場合、それを使います。
それ以外の場合は次の段階に進みます。
もしunknown
と既知の型の引数の両方があり、そして全ての既知の型の引数が同じ型を持っていた場合、unknown
引数も同じ型であると仮定し、
どの候補がunknown
引数の位置にある型を受け付けることができるかを検査します。
正確に1つの候補がこの検査を通過した場合、それを使います。それ以外は失敗します。
以下に例を示します。
例10.1 平方根演算子の型解決
平方根演算子として、double precision
を引数とするものが標準カタログ内に1つのみ定義されています(|/
を前に付けます)。
スキャナは、以下の問い合わせ式の引数にまず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 文字列連結演算子の型解決
文字列類似構文は、文字列の作業の他、複雑な拡張型の作業にも使用されます。 型の指定がない文字列は、類似演算子候補に一致します。
例えば、以下は指定がない引数が1つあります。
SELECT text 'abc' || 'def' AS "text and unknown"; text and unknown ------------------ abcdef (1 row)
この場合、パーサは両引数でtext
を取る演算子があるかどうかを検索します。
この演算子は存在しますので、第二引数はtext
型として解釈されるものと仮定されます。
以下は型の指定がない2つの値の連結です。
SELECT 'abc' || 'def' AS "unspecified"; unspecified ------------- abcdef (1 row)
この場合、問い合わせ内に型が指定されていませんので、どの型を使用すべきかについての初期の指針がありません。
ですから、パーサは全ての演算子候補を検索し、文字列カテゴリとビット列カテゴリ入力を受け付ける候補を見つけます。
使用できる場合は文字列カテゴリが優先されますので、文字列カテゴリが選択され、それから文字列に対して優先される型であるtext
が、不明のリテラルを解決する型として使用されます。
例10.3 絶対値と否定演算子の型解決
PostgreSQLの演算子カタログには、前置演算子@
用に複数の項目があります。
これは全て各種数値データ型に対する絶対値計算を実装するものです。
その1つは、数値カテゴリの優先される型であるfloat8
型用の項目です。
したがって、PostgreSQLは、unknown
の入力があった場合にこれを使用します。
SELECT @ '-4.5' AS "abs"; abs ----- 4.5 (1 row)
ここでシステムは、選択した演算子を適用する前に、unknown型のリテラルを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
)の2つのみです。
これらの多様な擬似データ型(8.21を参照)は優先されると見なされないため、このような方法ではパーサは曖昧さを解決することができません。
しかし、ステップ 3.fでは、未知の型のリテラルを別の入力と同じ型であると仮定するために数値配列とみなします。
今のところ2つのうち一つの演算子だけがマッチできるため、配列包含が選択されます。(範囲包含が選択された場合、演算子の右側にある文字列は正しい範囲型のリテラルではないため、エラーとなるでしょう。)
例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
演算子がすぐに見つかります。
もし、「最善の一致」規則に達した場合、ドメイン型の演算子を積極的に差別します。
そうでなければ、そのような演算子は非常に多くの「曖昧な演算子」の失敗を引き起こします。キャストの規則はドメインをその基本型からもしくは基本型へキャスト可能と考え、ドメイン演算子は基本型の似たような名前の演算子とすべて同じ状況で利用できると考えられるからです。