Подтвердить что ты не робот

DROP FUNCTION, не зная количество/тип параметров?

Я сохраняю все свои функции в текстовом файле с 'CREATE OR REPLACE FUNCTION somefunction'. Поэтому, если я добавляю или изменяю какую-то функцию, я просто пишу файл в psql.

Теперь, если я добавляю или удаляю параметры в существующую функцию, она создает перегрузку с тем же именем и для удаления исходного типа мне нужен тип во всех типах параметров в точном порядке, который является довольно утомительным.

Есть ли какой-нибудь подстановочный знак, который я могу использовать для DROP всех функций с заданным именем, поэтому я могу просто добавить строки DROP FUNCTION в начало моего файла?

4b9b3361

Ответ 1

Вам нужно будет написать функцию, которая взяла имя функции, и посмотрела каждую перегрузку с ее типами параметров из information_schema, а затем построила и выполнила DROP для каждого из них.

EDIT: Это оказалось намного сложнее, чем я думал. Похоже, что 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;

Я успешно проверил это на перегруженной функции. Он был сложен довольно быстро, но отлично работает как функция полезности. Я бы рекомендовал тестировать больше, прежде чем использовать его на практике, если я что-то упустил.

Ответ 2

Основной запрос

Этот запрос создает все необходимые операторы 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 (regprocedure oid::regprocedure) делает все идентификаторы безопасными от внедрения SQL (посредством вредоносных искаженных идентификаторов). При преобразовании в text имя функции заключается в двойные кавычки и search_path по схеме в соответствии с текущим search_path где это необходимо.

pg_function_is_visible(oid) ограничивает выбор функциями в текущем пути search_path. Вы можете или не можете этого хотеть. При наличии условия pg_function_is_visible(oid) функция гарантированно будет видимой.

Если у вас есть несколько функций с одним и тем же именем в нескольких схемах или перегружены функции с различными аргументами функций, все они будут перечислены отдельно. Вы можете хотеть ограничиться определенной схемой (ами) или определенным параметром (ами) функции в конце концов.

Связанные с:

функция

Вы можете построить функцию plpgsql вокруг этого, чтобы выполнить операторы немедленно с помощью EXECUTE. Для Postgres 9.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');

Таким образом, вы не получите имя столбца func_dropped для столбца результата. Может не важно для вас.

Функция возвращает количество найденных и удаленных функций (исключение не возникло) - 0 если ни одна не была найдена.

Он предполагает (по умолчанию) search_path где pg_catalog не был перемещен.
Больше в этих связанных ответах:

Для версий Postgres старше 9.1 или более старых вариантов функции, использующих regproc и pg_get_function_identity_arguments(oid) проверьте историю редактирования этого ответа.

Ответ 3

Улучшение исходного ответа, чтобы принять во внимание 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';

Ответ 4

Вот запрос, который я построил поверх решения @Сухой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);

Ответ 5

Начиная с Postgres 10, вы можете удалять функции только по имени, если имена уникальны для их схемы. Просто поместите следующее объявление вверху вашего файла функции:

drop function if exists my_func;

Документация здесь.

Ответ 6

Немного расширенная версия ответа 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');

Ответ 7

pgsql генерирует ошибку, если существует более одной процедуры с одинаковым именем, но разными аргументами, когда процедура удаляется в соответствии с ее именем. Таким образом, если вы хотите удалить одну процедуру, не затрагивая другие, просто используйте следующий запрос.

ВЫБЕРИТЕ "ФУНКЦИЮ КАПЛИ" || oid :: regprocedure ОТ pg_proc ГДЕ oid = {$ proc_oid}