PostgreSQL 9.4.5文書 | |||
---|---|---|---|
前のページ | 上に戻る | 第 10章型変換 | 次のページ |
演算式に参照される特定の演算子は、以下の手順を用いて決定されます。 関連する演算子の優先順位によりどの下位式をどの演算子の入力と見なすかが決定されますので、この手順はこの優先順位により間接的な影響を受けることに注意して下さい。 詳細は項4.1.6を参照してください。
演算子における型の解決
pg_operator
システムカタログから、調査の対象とする演算子を選択します。
スキーマ修飾がされていない演算子名が使用される場合(通常の場合)、現行の検索パスで可視になっていて、同一の名前と引数の数を持つ演算子が調査対象であるとみなされます
(項5.7.3を参照してください)。
修飾された演算子名が与えられている場合、指定されたスキーマの演算子のみが調査対象とみなされます。
検索パスで引数のデータ型が同じである複数の演算子を検出した場合、そのパスで最初に検出された演算子のみを調査対象とみなします。 引数のデータ型が異なる演算子は、検索パス内の位置に関係なく、同じように調べられます。
正確に入力引数型を受け付ける演算子があるかどうか検査します。 該当する演算子があれば(調査される演算子の集合内で正確に一致するものは1つしかあり得ません)、それを使用します。
最もよく合うものを検索します。
演算子の候補のうち、入力値のデータ型が一致せず、また、(暗黙的な変換を使用して)一致するように変換できないものを破棄します。 unknownリテラルは、上記の目的で何にでも変換可能とみなされます。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
入力引数のいずれかがドメイン型であれば、以降の段階すべてでドメインの基本型であるかのように扱います。 これにより、曖昧な演算子を解決するのを目的としてその基本型であるかのようにドメインが振る舞うことが確実になります。
全ての候補を検索し、入力型に最も正確に合うものを残します。 正確に合うものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
全ての候補を検索し、型変換が必要とされる所で(入力データ型カテゴリの)好ましい型を受け付けるものを残します。 好ましい型を受け付けるものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
入力引数でunknownのものがあった場合、それらの残った候補に引数位置で受け入れられる型カテゴリを検査します。 各位置において、候補がstringカテゴリを受け付ける場合は、そのカテゴリを選択します (unknown 型のリテラルは文字列のようなものですので、この文字列への重み付けは適切です)。 そうでなければ、もし残った全ての候補が同じ型カテゴリを受け入れる場合はそのカテゴリを選択します。 そうでもなければ、さらに手掛かりがなければ正しい選択が演繹されることができませんので、失敗となります。 ここで、選択された型カテゴリを受け付けない演算子候補は破棄されます。 さらに、それらカテゴリ内の好ましい型を受け付ける候補が1つでもある場合、その引数の好ましい型ではないものを受け付ける候補は破棄されます。 これらの検査をどれも通らなかったら全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
もしunknownと既知の型の引数の両方があり、そして全ての既知の型の引数が同じ型を持っていた場合、unknown引数も同じ型であると仮定し、 どの候補がunknown引数の位置にある型を受け付けることができるかを検査します。 正確に1つの候補がこの検査を通過した場合、それを使います。それ以外は失敗します。
以下に例を示します。
例 10-1. 階乗演算子の型解決
階乗演算子として、bigintを引数とするものが標準カタログ内に1つのみ定義されています(!を後に付けます)。 スキャナは、以下の問い合わせ式の引数にまずinteger型を割り当てます。
SELECT 40 ! AS "40 factorial"; 40 factorial -------------------------------------------------- 815915283247897734345611269596115894272000000000 (1 row)
パーサはオペランドを型変換し、問い合わせは以下と等価になります。
SELECT CAST(40 AS bigint) ! AS "40 factorial";
例 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.20を参照は好ましいと見なされないため、このような方法ではパーサは曖昧さを解決することができません。 しかし、ステップ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演算子が使われます。 独自の演算子を使う唯一の方法は、「正確な一致」規則に従ってmytext = text演算子がすぐに見つかるように、リテラルを明示的にキャストすることだけです。
SELECT * FROM mytable WHERE val = text 'foo';
もし、「最善の一致」規則に達した場合、ドメイン型の演算子を積極的に差別します。 そうでなければ、そのような演算子は非常に多くの「曖昧な演算子」の失敗を引き起こします。キャストの規則はドメインをその基本型からもしくは基本型へキャスト可能と考え、ドメイン演算子は基本型の似たような名前の演算子とすべて同じ状況で利用できると考えられるからです。