関数呼び出しによって参照される特定の関数は、以下の手順に従って解決されます。
関数における型の解決
pg_proc
システムカタログから、調査の対象とする関数を選択します。
スキーマ修飾がされていない関数名が使用される場合、現行の検索パスで可視になっていて、同一の名前と引数の数を持つ関数が調査対象であるとみなされます
(5.9.3を参照してください)。
修飾された関数名が与えられている場合、指定されたスキーマの関数のみが調査対象とみなされます。
検索パスで、引数のデータ型が同じである複数の関数を検出した場合、そのパスで最初に検出された関数のみを調査対象とみなします。 引数のデータ型が異なる関数は、検索パス内の位置に関係なく、同じように調べられます。
もし関数がVARIADIC
型の配列パラメータを伴って定義されており、そしてVARIADIC
キーワードを用いずに呼ばれた場合は、呼び出しに適合するよう、一つかそれ以上の要素の型に配列のパラメータを置き換えた形で扱われます。
このような拡張後は、その関数は実際の引数の型を持つので、他の非可変長の引数を持つ関数と同一になるかもしれません。この場合、検索パスで先に見つかった関数が使われます。また、同じスキーマに2つの関数が見つかった場合は非可変長の関数が優先されます。
信用できないユーザにオブジェクトの作成を許可しているスキーマで見つかる可変長引数の関数を、修飾された名前で
[10],
呼び出す時にセキュリティの危険が発生します。
悪意のあるユーザは、支配権を奪い、あたかもあなたが実行したかのように任意のSQL関数を実行できます。
VARIADIC
キーワードを持つ呼び出しを代わりに使ってください。そうすればこの危険は避けられます。
VARIADIC "any"
パラメータにデータを入れての呼び出しには、しばしば同等のVARIADIC
キーワードを含む定式化がありません。
この呼び出しを安全に行うには、関数のスキーマは信用できるユーザだけがオブジェクトを作成できるようにしなければなりません。
パラメータにデフォルト値を持つ関数は、デフォルト指定可能なパラメータ位置のうち、0以上が省略されたどのような呼び出しに対しても適合すると見なされます。 もし呼び出し時にこのような関数が2つ以上適合した場合、検索パスで先に見つかったものが使用されます。 もし、デフォルト指定のない位置に同じパラメータ型を持つ関数(もしそれらが異なるデフォルト指定のあるパラメータのセットを持っていればあり得ます)が同じスキーマに2つ以上あった時は、システムはどの関数を使うべきか決定できず、呼び出しにより適合するものが見つからなければ「ambiguous function call」エラーが結果として返るでしょう。
信用できないユーザにオブジェクトの作成を許可しているスキーマで見つかる関数を修飾された名前で[10]呼び出す時に、これは濫用の危険を起こします。 悪意のあるユーザは、既存の関数の名前で、その関数のパラメータを複製し、デフォルト値を持つ新しいパラメータを追加した関数を作成できます。 これは元の関数への新しい呼び出しを妨げます。 この危険を未然に防ぐには、関数を信用できるユーザだけがオブジェクトを作成できるスキーマに置いてください。
正確に入力引数型を受け付ける関数があるかどうか検査します。
該当する関数があれば(調査される関数の集合内で正確に一致するものは1つしかあり得ません)、それを使用します。
正確に一致するものがない場合、信用できないユーザにオブジェクトの作成を許可しているスキーマで見つかる関数を、修飾された名前で[10]呼び出す時にセキュリティの危険が発生します。
そのような状況では、強制的に正確に一致するように引数をキャストしてください。
(unknown
を含む場合は、この段階で一致するものは決して見つかりません。)
正確に一致するものが存在しなかった場合、その関数呼び出しが特別な型変換要求であるかどうかを確認します。
これは、関数呼び出しがただ1つの引数を取り、関数名が何らかのデータ型の(内部的な)名前と同一である場合に発生します。
さらに、その関数の引数は、unknown型のリテラルか指定されたデータ型へのバイナリ変換可能な型か、型の入出力関数を適用することで指定された型に変換可能な型(つまり、変換が標準文字列型との間の変換である)であるかのいずれかでなければなりません。
これらの条件に合う場合、関数呼び出しはCAST
仕様の形式と同様に扱われます。
[11]
最適なものを検索します。
関数の候補のうち、入力値のデータ型が一致せず、また、(暗黙的な変換を使用して)一致するように変換できないものを破棄します。
unknown
リテラルは、上記の目的で何にでも変換可能とみなされます。
1つの候補しか残らない場合、それを使います。
それ以外の場合は次の段階に進みます。
入力引数のいずれかがドメイン型であれば、以降の段階すべてでドメインの基本型であるかのように扱います。 これにより、曖昧な関数を解決するのを目的としてその基本型であるかのようにドメインが振る舞うことを確実にします。
全ての候補を検索し、入力型に最も正確に合うものを残します。 正確に合うものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
全ての候補を検索し、型変換が必要とされるところで(入力データ型カテゴリの)優先される型を受け付けるものを残します。 優先される型を受け付けるものが何もなければ全ての候補を残します。 1つの候補しか残らない場合、それを使います。 それ以外の場合は次の段階に進みます。
入力引数でunknown
のものがあった場合、それらの残った候補に引数位置で受け入れられる型カテゴリを検査します。
各位置で候補がstring
カテゴリを受け付ける場合は、そのカテゴリを選択します
(unknown 型のリテラルは文字列のようなものですので、この文字列への重み付けは適切です)。
そうでなければ、もし残った全ての候補が同じ型カテゴリを受け入れる場合はそのカテゴリを選択します。
そうでもなければ、さらに手掛かりがなければ正しい選択が演繹されることができませんので、失敗となります。
ここで、選択された型カテゴリを受け付けない演算子候補は破棄されます。
さらに、このカテゴリ内の優先される型を受け付ける候補が1つでもある場合、その引数の優先されない型を受け付ける候補は破棄されます。
これらの検査をどれも通らなかったら全ての候補を残します。
1つの候補しか残らない場合、それを使います。
それ以外の場合は次の段階に進みます。
もしunknown
と既知の型の引数の両方があり、そして全ての既知の型の引数が同じ型を持っていた場合、unknown
引数も同じ型であると仮定し、
どの候補がunknown
引数の位置にある型を受け付けることができるかを検査します。
正確に1つの候補がこの検査を通過した場合、それを使います。それ以外は失敗します。
この「最善一致」規則は演算子と関数の型解決で同一であることに注意してください。 以下に例を示します。
例10.6 丸め関数引数の型解決
2つの引数を取るround
関数は1つしかありません
第1引数としてnumeric
型、第2引数としてinteger
型を取ります。
ですから、以下の問い合わせは自動的に、integer
型の第1引数をnumeric
に変換します。
SELECT round(4, 4); round -------- 4.0000 (1 row)
問い合わせはパーサによって実質以下のように変形されます。
SELECT round(CAST (4 AS numeric), 4);
小数点を持つ数値定数はまずnumeric
に割り当てられますので、以下の問い合わせでは型変換が不要です。そのためかなり効率的になる可能性があります。
SELECT round(4.0, 4);
例10.7 可変長引数の関数の解決
CREATE FUNCTION public.variadic_example(VARIADIC numeric[]) RETURNS int LANGUAGE sql AS 'SELECT 1'; CREATE FUNCTION
この関数は、必須ではないですがVARIADICキーワードを受け付けます。 整数の引数と数値の引数の両方を許容します。
SELECT public.variadic_example(0), public.variadic_example(0.0), public.variadic_example(VARIADIC array[0.0]); variadic_example | variadic_example | variadic_example ------------------+------------------+------------------ 1 | 1 | 1 (1 row)
しかしながら、1番目と2番目の呼び出しは、もし利用可能なら、より特定の関数を優先します。
CREATE FUNCTION public.variadic_example(numeric) RETURNS int LANGUAGE sql AS 'SELECT 2'; CREATE FUNCTION CREATE FUNCTION public.variadic_example(int) RETURNS int LANGUAGE sql AS 'SELECT 3'; CREATE FUNCTION SELECT public.variadic_example(0), public.variadic_example(0.0), public.variadic_example(VARIADIC array[0.0]); variadic_example | variadic_example | variadic_example ------------------+------------------+------------------ 3 | 2 | 1 (1 row)
もしデフォルトの設定で最初の関数だけが存在しているなら、1番目と2番目の呼び出しは安全ではありません。
ユーザは、2番目や3番目の関数を作成することで、それらを妨害できます。
引数の型を厳密に一致させVARIADIC
キーワードを使うのなら、3番目の呼び出しは安全です。
例10.8 部分文字列関数の型解決
substr
関数は複数存在します。
その1つはtext
とinteger
型を取ります。
型の指定がない文字列定数で呼び出した場合、システムは優先されるカテゴリstring
(すなわちtext
型)の引数を受け付ける候補関数を選択します。
SELECT substr('1234', 3); substr -------- 34 (1 row)
文字列がvarchar
型と宣言された場合、これはテーブルから取り出した場合が考えられますが、パーサはそれをtext
になるように変換しようと試みます。
SELECT substr(varchar '1234', 3); substr -------- 34 (1 row)
これは以下になるようにパーサによって変換されます。
SELECT substr(CAST (varchar '1234' AS text), 3);
パーサはpg_cast
カタログからtext
とvarchar
がバイナリ互換、つまり、何らかの物理的な変換を行うことなく片方を受け付ける関数にもう片方を渡すことができることを知ります。
したがって、この場合実際に挿入される型変換呼び出しはありません。
また、integer
型の引数でこの関数が呼び出された場合、パーサはそれをtext
に変換しようと試みます。
SELECT substr(1234, 3); ERROR: function substr(integer, integer) does not exist HINT: No function matches the given name and argument types. You might need to add explicit type casts.
integer
はtext
への暗黙的なキャストを持たないため、これは失敗します。
成功させるには、以下のように明示的なキャストを行います。
SELECT substr(CAST (1234 AS text), 3); substr -------- 34 (1 row)
[10] 信用できないユーザにオブジェクトの作成を許可するスキーマを含む検索パスは、安全なスキーマ使用パターンではありませんので、スキーマで修飾されていない名前では危険は起こりません。
[11] この処理の理由は、実際にはキャスト関数が存在しない状況において、関数形態のキャスト仕様をサポートすることです。 キャスト関数が存在する場合、慣習的に出力型に因んで名付けられます。 ですので、特殊な状況を持つ必要はありません。 詳細な解説についてはCREATE CASTを参照してください。