2

1 つのデータベースに、同じ関数名を持つ 2 つの類似したスキーマがあります。各スキーマは、スキーマ名に一致するロールによって所有されます。

ネストされた関数での関数名の解決に問題があります。外側の関数が同じスキーマ内の内側の関数を呼び出すことを期待していましたが、そうではありません! 名前は、実行時に search_path に基づいて動的に解決されます。

これがテストケースです。たとえば、次のようにスキーマとロールに test と prod という名前が付けられているとします。

テスト スキーマ:
CREATE ROLE test NOLOGIN;
CREATE SCHEMA test AUTHORIZATION test;

CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
   RETURN 'test function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.inner_func() OWNER TO test;

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
   RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.outer_func() OWNER TO test;
製品スキーマ:
CREATE ROLE prod NOLOGIN;
CREATE SCHEMA prod AUTHORIZATION prod;

CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
   RETURN 'prod function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.inner_func() OWNER TO prod;

CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
   RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.outer_func() OWNER TO prod;
テストケース:
SET search_path=test,public;    
SELECT outer_func();
> test function

SELECT prod.outer_func();
> test function <<<---- was expecting prod function 

SET search_path=prod,public;
SELECT prod.outer_func();
> prod function

search_pathこのテストは、関数名が実行時に基づいて動的に解決されることを示しています。スキーマのスコープ内で内部関数をバインドする方法はありますか?

SECURITY DEFINER動的 SQL および で関数を使用することでこのような動作を得ることができますが、CURRENT_USERもっと簡単なものを探しています。

4

1 に答える 1

0

クリーンな解決策は、関数をスキーマ修飾することです。

CREATE OR REPLACE FUNCTION test.outer_func()
  RETURNS SETOF text AS
$func$
BEGIN
   RETURN QUERY SELECT test.inner_func();
END
$func$  LANGUAGE plpgsql;  -- no quotes!

search_pathまたは、 per 関数を明示的に設定します。この方法で構成パラメーターを設定できます

CREATE OR REPLACE FUNCTION test.outer_func()
  RETURNS SETOF text AS
$func$
BEGIN
   RETURN QUERY SELECT inner_func();
END
$func$  LANGUAGE plpgsql SET search_path = test, pg_temp;

必要に応じてカスタマイズし、リストsearch_pathに追加publicしてください。最後に pg_temp を配置したので、一時スキーマ内のオブジェクトは永続化されたオブジェクトを隠すことができません。(ただし、それは関数には適用されません。)関数のマニュアルでSECURITY DEFINER説明されている内容と同様です。

ユーザーが適切に設定することに依存することはお勧めしませsearch_path。それは「パブリック」機能に対してのみ意味があり、設計と一致しません。個別の関数を作成し、ユーザー設定に依存する必要があるのはなぜですか? そもそも public スキーマに 1 つの関数を含めることもできますが、いずれにしてもそのルートは得られません。非常に紛らわしく、エラーが発生しやすい。

また、PL/pgSQL は内部で準備済みステートメントのようなステートメントを実行します。search_path を変更するたびに、plpgsql 関数からのすべての「準備された」ステートメントの割り当てを解除する必要があり、パフォーマンスの最適化には役立たない。

実際、質問のテストケースは、最初に設定した場合にのみ機能しますsearch_path:

SET search_path=test,public;

そうしないと、作成しようとするとエラーが発生します

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
   RETURN QUERY SELECT inner_func();
...
ERROR: function inner_func() does not exist

構文チェックはsearch_path、作成時に現在のものに対して実行されます-提案どおりに提供しない限り。バグを報告した後、2010年に修正されました。search_path

の詳細search_path:

また、言語名を引用しないでください。識別子です。

于 2015-05-02T13:32:33.843 に答える