web-dev-qa-db-ja.com

関数がPostgreSQLに存在するかどうかを確認するにはどうすればよいですか?

テーブルやシーケンスとは異なり、ユーザー定義関数は pg_class では見つかりません。 削除するすべての関数のリストを見つける方法 または 付与 それらについて質問がありますが、(既知の名前と引数の型を持つ)個々の関数を見つける方法は自己ではありません-それらから明らかです。それでは、関数が存在するかどうかをどのように見つけるのでしょうか?

編集:私は関数で、自動化された方法で使用したいと思います。どのソリューションがパフォーマンス面で最高ですか?エラーのトラップは非常にコストがかかるので、エラーをfalseに変換する追加の手順がないものが最善の解決策になると思いますが、この仮定では間違っている可能性があります。

14
Pavel V.

はい、関数はシステムテーブルpg_classに格納されているため、pg_procで関数を見つけることはできません。

postgres-# \df
                               List of functions
 Schema |        Name        | Result data type | Argument data types  |  Type  
--------+--------------------+------------------+----------------------+--------
 public | foo                | integer          | a integer, b integer | normal
 public | function_arguments | text             | oid                  | normal
(2 rows)

pg_procに基づくカスタム関数のリストのクエリは単純です

postgres=# select p.oid::regprocedure
              from pg_proc p 
                   join pg_namespace n 
                   on p.pronamespace = n.oid 
             where n.nspname not in ('pg_catalog', 'information_schema');
           oid           
-------------------------
 foo(integer,integer)
 function_arguments(oid)
(2 rows)

関数の存在に関する最も単純で最速のテストは、(パラメーターなしで)regprocまたはregprocedure(パラメーター付き)にキャストすることです。

postgres=# select 'foo'::regproc;
 regproc 
---------
 foo
(1 row)

postgres=# select 'foox'::regproc;
ERROR:  function "foox" does not exist
LINE 1: select 'foox'::regproc;
               ^
postgres=# select 'foo(int, int)'::regprocedure;
     regprocedure     
----------------------
 foo(integer,integer)
(1 row)

postgres=# select 'foo(int, text)'::regprocedure;
ERROR:  function "foo(int, text)" does not exist
LINE 1: select 'foo(int, text)'::regprocedure;
               ^

または、pg_procに対するテストで同様のことを行うことができます

postgres=# select exists(select * from pg_proc where proname = 'foo');
 exists 
--------
 t
(1 row)

postgres=# select exists(select * 
                            from pg_proc 
                           where proname = 'foo' 
                             and function_arguments(oid) = 'integer, integer');
 exists 
--------
 t
(1 row)

どこ:

CREATE OR REPLACE FUNCTION public.function_arguments(oid)
RETURNS text LANGUAGE sql AS $function$
    select string_agg(par, ', ') 
       from (select format_type(unnest(proargtypes), null) par 
                from pg_proc where oid = $1) x
$function$

または、組み込み関数を使用できます:pg_get_function_arguments

pSシステムカタログで単に方向付けするためのトリック。 psqlオプションを使用-E

[pavel@localhost ~]$ psql -E postgres
psql (9.2.8, server 9.5devel)
Type "help" for help.

postgres=# \df
********* QUERY **********
SELECT n.nspname as "Schema",
  p.proname as "Name",
  pg_catalog.pg_get_function_result(p.oid) as "Result data type",
  pg_catalog.pg_get_function_arguments(p.oid) as "Argument data types",
 CASE
  WHEN p.proisagg THEN 'agg'
  WHEN p.proiswindow THEN 'window'
  WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'
  ELSE 'normal'
END as "Type"
FROM pg_catalog.pg_proc p
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE pg_catalog.pg_function_is_visible(p.oid)
      AND n.nspname <> 'pg_catalog'
      AND n.nspname <> 'information_schema'
ORDER BY 1, 2, 4;
**************************

                               List of functions
 Schema |        Name        | Result data type | Argument data types  |  Type  
--------+--------------------+------------------+----------------------+--------
 public | foo                | integer          | a integer, b integer | normal
 public | function_arguments | text             | oid                  | normal
(2 rows)
33
Pavel Stehule

pg_get_functiondef()を使用するのが最も簡単な方法だと思います。

それが何かを返す場合、関数はそこにあり、そうでなければ関数は存在しません:

select pg_get_functiondef('some_function()'::regprocedure);
select pg_get_functiondef('some_function(integer)'::regprocedure);

欠点は、単に空の結果を返すのではなく、関数が存在しない場合にエラーが発生することです。しかし、これは例えば例外をキャッチして代わりにfalseを返すPL/pgSQL関数を作成することで克服できます。

@PavelStehuleの回答に基づいて、これはスクリプトでこれをチェックする方法です( postgres exceptions および 利用可能な例外コード を使用)。

DO $_$
BEGIN
    BEGIN
        SELECT 'some_schema.some_function(text)'::regprocedure;
    EXCEPTION WHEN undefined_function THEN
        -- do something here, i.e. create function
    END;
END $_$;

0
Greg0ry