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

发布于 2024-12-07 11:08:08 字数 290 浏览 1 评论 0原文

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

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

是否可以使用某种通配符来 DROP 具有给定名称的所有函数,以便我可以将 DROP FUNCTION 行添加到文件顶部?

I keep all my functions in a text file with 'CREATE OR REPLACE FUNCTION somefunction'.
So if I add or change some function I just feed the file to psql.

Now if I add or remove parameters to an existing function, it creates an overload with the same name and to delete the original I need type in all the parameter types in the exact order which is kind of tedious.

Is there some kind of wildcard I can use to DROP all functions with a given name so I can just add DROP FUNCTION lines to the top of my file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

两相知 2024-12-14 11:08:08

基本查询

此查询创建所有必需的 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),然后隐式地生成带有参数类型的函数名称双引号和模式限定根据当前的 search_path(在需要的地方)。 不可能进行 SQL 注入

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

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

相关:

函数

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

小心!它会删除您的函数!

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

进一步阅读:

< em>对于 9.1 之前的 Postgres 版本或使用 regproc 的旧版本函数以及pg_get_function_identity_arguments(oid) 检查此答案的编辑历史记录。

Basic query

This query creates all necessary DDL statements:

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

Output:

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

Execute the commands after checking plausibility.

Pass the function name case-sensitive and with no added double-quotes to match against pg_proc.proname.

The cast to the object identifier type regprocedure (oid::regprocedure), and then to text implicitly, produces function names with argument types, automatically double-quoted and schema-qualified according to the current search_path where needed. No SQL injection possible.

pg_function_is_visible(oid) restricts the selection to functions in the current search_path ("visible"). You may or may not want this.

If you have multiple functions of the same name in multiple schemas, or overloaded functions with various function arguments, all of those will be listed separately. You may want to restrict to specific schema(s) or specific function parameter(s).

Related:

Function

You can build a PL/pgSQL function around this to execute the statements immediately with EXECUTE.

Careful! It drops your functions!

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

Call:

SELECT f_delfunc('my_function_name');

The function returns the number of functions found and dropped if no exceptions are raised. 0 if none were found.

Further reading:

For Postgres versions older than 9.1 or older variants of the function using regproc and pg_get_function_identity_arguments(oid) check the edit history of this answer.

心奴独伤 2024-12-14 11:08:08

您需要编写一个函数,该函数采用函数名称,并从 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;

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

You would need to write a function that took the function name, and looked up each overload with its parameter types from information_schema, then built and executed a DROP for each one.

EDIT: This turned out to be a lot harder than I thought. It looks like information_schema doesn't keep the necessary parameter information in its routines catalog. So you need to use PostgreSQL's supplementary tables pg_proc and 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;

I successfully tested this on an overloaded function. It was thrown together pretty fast, but works fine as a utility function. I would recommend testing more before using it in practice, in case I overlooked something.

鹤仙姿 2024-12-14 11:08:08

改进原始答案以考虑架构,即。 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';

Improving original answer in order to take schema into account, ie. 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';
残月升风 2024-12-14 11:08:08

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

  • “like”而不是精确的函数名称匹配,
  • 可以在“dry-mode”和“trace”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');

Slightly enhanced version of Erwin's answer. Additionally supports following

  • 'like' instead of exact function name match
  • can run in 'dry-mode' and 'trace' the SQL for removing of the functions

Code for copy/paste:

/**
 * 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');
◇流星雨 2024-12-14 11:08:08

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

Here is the query I built on top of @Сухой27 solution that generates sql statements for dropping all the stored functions in a schema:

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);
微暖i 2024-12-14 11:08:08

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

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

pgsql generates an error if there exists more than one procedure with the same name but different arguments when the procedure is deleted according to its name. Thus if you want to delete a single procedure without affecting others then simply use the following query.

SELECT 'DROP FUNCTION ' || oid::regprocedure
FROM   pg_proc
WHERE  oid = {$proc_oid}
樱花落人离去 2024-12-14 11:08:08

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

drop function if exists my_func;

文档此处

As of Postgres 10 you can drop functions by name only, as long as the names are unique to their schema. Just place the following declaration at the top of your function file:

drop function if exists my_func;

Documentation here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文