在不知道参数数量/类型的情况下删除函数?

问题描述 投票:0回答:7

我将所有函数保存在一个带有

'CREATE OR REPLACE FUNCTION somefunction'
的文本文件中。
因此,如果我添加或更改某些函数,我只需将文件提供给 psql。

现在,如果我向现有函数添加或删除参数,它会创建一个具有相同名称的重载,并且要删除原始函数,我需要按确切的顺序输入所有参数类型,这有点乏味。

是否可以使用某种通配符来

DROP
具有给定名称的所有函数,以便我可以将
DROP FUNCTION
行添加到文件顶部?

sql postgresql plpgsql dynamic-sql ddl
7个回答
105
投票

基本查询

此查询创建所有必需的 DDL 语句:

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

输出:

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);

检查合理性后执行命令。

传递函数名称区分大小写且不添加双引号以与

pg_proc.proname
匹配。

强制转换为对象标识符类型

regprocedure
(
oid::regprocedure
),然后隐式转换为
text
,生成带有参数类型的函数名称,根据当前的情况自动使用双引号和模式限定
search_path
需要的地方。 不可能进行 SQL 注入

pg_function_is_visible(oid)
将选择限制为当前
search_path
中的功能(“可见”)。您可能想要也可能不想要这个。

如果您在多个模式中有多个同名函数,或者具有各种函数参数的重载函数,则其中的 所有 将单独列出。您可能希望限制为特定模式或特定函数参数。

相关:

功能

您可以围绕此构建一个 PL/pgSQL 函数,以使用

EXECUTE
立即执行语句。

小心!它会降低你的功能!

CREATE OR REPLACE FUNCTION f_delfunc(_name text, OUT functions_dropped int)
   LANGUAGE plpgsql AS
$func$
-- drop all functions with given _name in the current search_path, regardless of function parameters
DECLARE
   _sql text;
BEGIN
   SELECT count(*)::int
        , 'DROP FUNCTION ' || string_agg(oid::regprocedure::text, '; DROP FUNCTION ')
   FROM   pg_catalog.pg_proc
   WHERE  proname = _name
   AND    pg_function_is_visible(oid)  -- restrict to current search_path
   INTO   functions_dropped, _sql;     -- count only returned if subsequent DROPs succeed

   IF functions_dropped > 0 THEN       -- only if function(s) found
     EXECUTE _sql;
   END IF;
END
$func$;

致电:

SELECT f_delfunc('my_function_name');

如果没有引发异常,该函数将返回找到并删除的函数的数量。

0
如果没有找到。

进一步阅读:

对于 9.1 之前的 Postgres 版本或使用

regproc
pg_get_function_identity_arguments(oid)
的旧版本函数,请检查此答案的编辑历史记录。


25
投票

您需要编写一个函数,该函数采用函数名称,并从

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;

我在重载函数上成功测试了这一点。它的组装速度相当快,但作为一个实用函数工作得很好。我建议在实践中使用它之前进行更多测试,以防我忽略了一些东西。


7
投票

改进原始答案以考虑

schema
,即。
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';

2
投票

埃尔文答案的稍微增强版本。另外支持以下

  • “like”而不是精确的函数名称匹配
  • 可以在“干模式”下运行并“跟踪”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
投票

这是我在@Сухой27解决方案之上构建的查询,它生成sql语句以删除模式中的所有存储函数:

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
投票

从 Postgres 10 开始,您只能按名称删除函数,只要名称对于其架构是唯一的。只需将以下声明放在函数文件的顶部即可:

drop function if exists my_func;

文档这里


1
投票

根据名称删除过程时,如果存在多个同名但参数不同的过程,pgsql 会产生错误。因此,如果您想删除单个过程而不影响其他过程,则只需使用以下查询即可。

SELECT 'DROP FUNCTION ' || oid::regprocedure
FROM   pg_proc
WHERE  oid = {$proc_oid}
© www.soinside.com 2019 - 2024. All rights reserved.