【问题标题】:DROP FUNCTION without knowing the number/type of parameters?DROP FUNCTION 不知道参数的数量/类型?
【发布时间】:2011-11-29 04:33:59
【问题描述】:

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

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

是否可以使用某种通配符来删除具有给定名称的所有函数,这样我就可以在文件顶部添加 DROP FUNCTION 行?

【问题讨论】:

  • 嗨 Steinthor,您介意接受 Erwin's answer 吗?我想删除我的以支持他的。

标签: postgresql plpgsql dynamic-sql sql-drop


【解决方案1】:

基本查询

此查询创建所有必要的 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(“可见”)中的函数。你可能想要也可能不想要这个。

如果您在多个架构中有多个同名函数,或者具有不同函数参数的重载函数,所有将分别列出。您可能希望限制为特定模式或特定函数参数。

相关:

功能

您可以围绕此构建一个plpgsql 函数以立即使用EXECUTE 执行语句。对于 Postgres 9.1 或更高版本: 小心!它会降低你的功能!

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 如果没有找到。

进一步阅读:

对于使用 regprocpg_get_function_identity_arguments(oid) 的函数的 9.1 或更早版本的 Postgres 版本,请检查此答案的编辑历史记录。

【讨论】:

  • +1 错过pg_catalog.pg_get_function_arguments() 并手动完成:/
  • 我发现psql -E 非常有用。这就是我想到 pg_catalog.pg_get_function_arguments(p.oid) 的地方。
  • 如果某些参数有默认值,这将不起作用,因为 pg_get_function_arguments 返回完整的 varname vartype default 语法,该语法在 DROP FUNCTION 中不可用,并且会引发语法错误
  • @JackDouglas:不,不是。我添加了更多解释。
  • @Andrus:我更新了一个改进的功能来解决这个问题。
【解决方案2】:

您需要编写一个获取函数名的函数,并从information_schema 中查找每个重载及其参数类型,然后为每个重载构建并执行DROP

编辑:这比我想象的要难得多。看起来information_schema 没有在其routines 目录中保留必要的参数信息。所以需要用到PostgreSQL的补充表pg_procpg_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;

我在一个重载函数上成功地测试了这个。它很快就被组合在一起了,但作为一个实用函数工作得很好。我建议在实际使用之前进行更多测试,以防我忽略了什么。

【讨论】:

  • 哇,谢谢。这很好用。猜猜它不像我想象的那么微不足道:P
  • 是的,虽然我错过了@Erwin's answer 提到的pg_catalog.pg_get_function_arguments()。如果你想让它看起来更优雅,你可以将两者结合起来。
【解决方案3】:

改进原始answer 以考虑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';

【讨论】:

  • 由于某种原因oid::regproc::text 并不总是有架构前缀
  • @basin:文本表示取决于当前的search_path 设置。仅在必要时为模式添加前缀。您可以在事务中 SET LOCAL search_path = '' 以获取所有对象名称模式限定。 (您还必须对同一事务中的所有对象进行模式限定。)
【解决方案4】:

Erwin 答案的略微增强版本。另外支持关注

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

【讨论】:

  • 危险功能。 dryrun 应该是默认方式,而不是其他方式
  • 我不确定进行“喜欢”匹配是否是个好主意。如果您不知道函数的确切名称,为什么要删除它?
  • 'like' 当你想杀死事先不知道他们名字的函数组时需要。在我的特殊情况下,与实体XXX 相关并具有XXX_create|update|delete|doSomting 之类的名称的所有函数都在文件 XXX.sql 中定义 - 在该脚本的开头,我有函数调用,它删除了所有以前版本的“XXX”函数。这在重命名函数时特别有用 - 例如XXX_doSomting -> XXX_doSomething.
【解决方案5】:

这是我在 @Сухой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);

【讨论】:

    【解决方案6】:

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

    drop function if exists my_func;
    

    文档here

    【讨论】:

    • 这在 11 中不起作用。我使用的是 V11.10。下面的语句当我在 pgadmin DROP PROCEDURE IF EXISTS create_account 中执行时;它只寻找零参数的过程或乐趣。因此,尽管文档状态,它不会丢弃不同的参数。
    【解决方案7】:

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 2021-08-11
      • 1970-01-01
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 2022-10-31
      • 2021-10-22
      • 2023-01-25
      相关资源
      最近更新 更多