web-dev-qa-db-ja.com

パラメータの数/タイプを知らずにDROP FUNCTION?

すべての関数を'CREATE OR REPLACE FUNCTION somefunction'のテキストファイルに保存します。したがって、関数を追加または変更した場合は、ファイルをpsqlにフィードするだけです。

ここで、既存の関数にパラメーターを追加または削除すると、同じ名前のオーバーロードが作成され、元のパラメーターをすべてのパラメーター型に正確な順序で入力する必要があります。

特定の名前のすべての関数を削除するために使用できるワイルドカードの種類はありますか?ファイルの先頭にDROP FUNCTION行を追加できますか?

43

関数名を取得し、information_schemaのパラメータータイプを使用して各オーバーロードを検索する関数を作成し、それぞれに対してDROPを作成して実行する必要があります。

編集:これは思ったよりずっと難しいことがわかりました。 information_schemaは、必要なパラメータ情報をroutinesカタログに保持していないようです。したがって、PostgreSQLの補足テーブルpg_procおよびpg_typeを使用する必要があります。

CREATE OR REPLACE FUNCTION udf_dropfunction(functionname text)
  RETURNS text AS
$BODY$
DECLARE
    funcrow RECORD;
    numfunctions smallint := 0;
    numparameters int;
    i int;
    paramtext text;
BEGIN
FOR funcrow IN SELECT proargtypes FROM pg_proc WHERE proname = functionname LOOP

    --for some reason array_upper is off by one for the oidvector type, hence the +1
    numparameters = array_upper(funcrow.proargtypes, 1) + 1;

    i = 0;
    paramtext = '';

    LOOP
        IF i < numparameters THEN
            IF i > 0 THEN
                paramtext = paramtext || ', ';
            END IF;
            paramtext = paramtext || (SELECT typname FROM pg_type WHERE oid = funcrow.proargtypes[i]);
            i = i + 1;
        ELSE
            EXIT;
        END IF;
    END LOOP;

    EXECUTE 'DROP FUNCTION ' || functionname || '(' || paramtext || ');';
    numfunctions = numfunctions + 1;

END LOOP;

RETURN 'Dropped ' || numfunctions || ' functions';
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

これをオーバーロードされた関数で正常にテストしました。それは一緒にかなり速く投げられましたが、ユーティリティ関数としてうまく機能します。私が何かを見落とした場合に備えて、実際に使用する前にさらにテストすることをお勧めします。

22
Paul Bellora

基本的なクエリ

このクエリは、必要なすべてのDDLステートメントを作成します( regprocedure へのキャストで簡略化されています)。

_SELECT 'DROP FUNCTION ' || oid::regprocedure
FROM   pg_proc
WHERE  proname = 'my_function_name'  -- name without schema-qualification
AND    pg_function_is_visible(oid);  -- restrict to current search_path ..
                                     -- .. you may or may not want this
_

出力:

_DROP FUNCTION my_function_name(string text, form text, maxlen integer);
DROP FUNCTION my_function_name(string text, form text);
DROP FUNCTION my_function_name(string text);
_

コマンドを実行します(妥当性チェック後)。

関数名は大文字と小文字が区別され、textパラメータとして渡されたときに二重引用符が追加されず、_pg_proc.proname_と照合されます。

オブジェクト識別子タイプregprocedure_oid::regprocedure_)にキャストすると、すべての識別子になりますSQLインジェクションに対して安全です(悪意のある不正な識別子による)。 textに変換する場合、関数名は二重引用符で囲まれ、必要に応じて現在の _search_path_ に従って自動的にスキーマ修飾されます。

pg_function_is_visible(oid) 選択を現在の関数に制限します _search_path_ 。あなたはそれを望むかもしれないし、望まないかもしれません。条件pg_function_is_visible(oid)を設定すると、関数が表示されることが保証されます。

複数のスキーマに同じ名前の複数の関数がある場合、またはさまざまな関数引数を持つオーバーロードされた関数がある場合は、allそれらの個別に表示されます。結局のところ、特定のスキーマまたは特定の関数パラメーターに制限したい場合があります。

関連:

関数

この周りにplpgsql関数を作成して、 EXECUTE を指定してステートメントをすぐに実行できます。 Postgres9.1以降の場合:注意!それはあなたの機能を落とします!

_CREATE OR REPLACE FUNCTION f_delfunc(_name text, OUT func_dropped int) AS
$func$
DECLARE
   _sql text;
BEGIN
   SELECT count(*)::int
        , 'DROP FUNCTION ' || string_agg(oid::regprocedure::text, '; DROP FUNCTION ')
   FROM   pg_proc
   WHERE  proname = _name
   AND    pg_function_is_visible(oid)
   INTO   func_dropped, _sql;  -- only returned if trailing DROPs succeed

   IF func_dropped > 0 THEN    -- only if function(s) found
     EXECUTE _sql;
   END IF;
END
$func$ LANGUAGE plpgsql;
_

コール:

_SELECT * FROM f_delfunc('my_function_name');
_

あるいは単に:

_SELECT f_delfunc('my_function_name');
_

この方法では、結果列の列name_func_dropped_を取得しません。あなたには関係ないかもしれません。

関数は、見つかって削除された関数の数を返します(例外は発生しません)-見つからなかった場合は_0_。

(デフォルト)_search_path_を想定していますが、_pg_catalog_は移動されていません。
これらの関連する回答の詳細:

regprocおよびpg_get_function_identity_arguments(oid)を使用した関数の9.1以前のバージョンより古いバージョンのPostgresの場合、この回答の編集履歴を確認してください。

70

schemaを考慮に入れるために、元の answer を改善します。 schema.my_function_name

select
    format('DROP FUNCTION %s(%s);',
      p.oid::regproc, pg_get_function_identity_arguments(p.oid))
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE
    p.oid::regproc::text = 'schema.my_function_name';
4
Сухой27

Postgres 10以降、名前がスキーマに対して一意である限り、関数は名前でのみ削除できます。関数ファイルの先頭に次の宣言を配置するだけです。

drop function if exists my_func;

ドキュメント ここ

2
Mark McKelvy

pgsqlは、名前に従ってプロシージャが削除されたときに、同じ名前で引数が異なるプロシージャが複数存在する場合にエラーを生成します。したがって、他のプロシージャに影響を与えずに1つのプロシージャを削除する場合は、次のクエリを使用します。

SELECT 'DROP FUNCTION ' || oid::regprocedure
FROM   pg_proc
WHERE  oid = {$proc_oid}

これは、スキーマに格納されているすべての関数を削除するためのSQLステートメントを生成する@Сухой27ソリューションの上に構築したクエリです。

WITH f AS (SELECT specific_schema || '.' || ROUTINE_NAME AS func_name 
        FROM information_schema.routines
        WHERE routine_type='FUNCTION' AND specific_schema='a3i')
SELECT
    format('DROP FUNCTION %s(%s);',
      p.oid::regproc, pg_get_function_identity_arguments(p.oid))
FROM pg_catalog.pg_proc p
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE
    p.oid::regproc::text IN (SELECT func_name FROM f);
1
Bo Guo

アーウィンの回答のわずかに強化されたバージョン。さらに以下をサポート

  • 関数名が完全に一致する代わりに「好き」
  • 関数を削除するために、「ドライモード」で実行し、SQLを「トレース」できます。

コピー/貼り付けのコード:

/**
 * Removes all functions matching given function name mask
 *
 * @param p_name_mask   Mask in SQL 'like' syntax
 * @param p_opts        Combination of comma|space separated options:
 *                        trace - output SQL to be executed as 'NOTICE'
 *                        dryrun - do not execute generated SQL
 * @returns             Generated SQL 'drop functions' string
 */
CREATE OR REPLACE FUNCTION mypg_drop_functions(IN p_name_mask text,
                                               IN p_opts text = '')
    RETURNS text LANGUAGE plpgsql AS $$
DECLARE
    v_trace boolean;
    v_dryrun boolean;
    v_opts text[];
    v_sql text;
BEGIN
    if p_opts is null then
        v_trace = false;
        v_dryrun = false;
    else
        v_opts = regexp_split_to_array(p_opts, E'(\\s*,\\s*)|(\\s+)');
        v_trace = ('trace' = any(v_opts)); 
        v_dryrun = ('dry' = any(v_opts)) or ('dryrun' = any(v_opts)); 
    end if;

    select string_agg(format('DROP FUNCTION %s(%s);', 
        oid::regproc, pg_get_function_identity_arguments(oid)), E'\n')
    from pg_proc
    where proname like p_name_mask
    into v_sql;

    if v_sql is not null then
        if v_trace then
            raise notice E'\n%', v_sql;
        end if;

        if not v_dryrun then
            execute v_sql;
        end if;
    end if;

    return v_sql;
END $$;

select mypg_drop_functions('fn_dosomething_%', 'trace dryrun');
1
Xtra Coder