web-dev-qa-db-fra.com

DROP FUNCTION sans connaître le nombre/type de paramètres?

Je garde toutes mes fonctions dans un fichier texte avec 'CREATE OR REPLACE FUNCTION somefunction'. Donc, si j'ajoute ou modifie une fonction, je nourris simplement le fichier dans psql.

Maintenant, si j'ajoute ou supprime des paramètres à une fonction existante, cela crée une surcharge avec le même nom et supprime le type d'origine nécessaire dans tous les types de paramètres dans l'ordre exact, ce qui est fastidieux.

Existe-t-il une sorte de caractère générique que je peux utiliser pour supprimer toutes les fonctions portant le même nom afin que je puisse simplement ajouter des lignes DROP FUNCTION en haut de mon fichier?

38

Vous devez écrire une fonction qui prend le nom de la fonction et examine chaque surcharge avec ses types de paramètres à partir de information_schema, puis crée et exécute une DROP pour chacun.

EDIT: / Cela s'est avéré être beaucoup plus difficile que je pensais. Il semble que information_schema ne conserve pas les informations de paramètre nécessaires dans son catalogue routines. Vous devez donc utiliser les tables supplémentaires pg_proc et pg_type de PostgreSQL:

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;

Je l'ai testé avec succès sur une fonction surchargée. Il a été assemblé assez rapidement, mais fonctionne très bien comme fonction d’utilité. Je recommanderais de tester davantage avant de l'utiliser dans la pratique, au cas où j'oublierais quelque chose.

21
Paul Bellora

Requête de base

Cette requête crée toutes les instructions DDL nécessaires (simplifiées avec la conversion en 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

Sortie:

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

Exécutez les commandes (après un contrôle de plausibilité).

Le nom de la fonction est sensible à la casse et ne contient pas de guillemets doubles lorsqu'il est transmis en tant que paramètre text à faire correspondre à pg_proc.proname.

Le transtypage en type d'identifiant d'objet regprocedure (oid::regprocedure) rend tous les identifiants safe contre l'injection SQL (au moyen d'identifiants malformés). Lors de la conversion en text, le nom de la fonction est cité entre guillemets et qualifié du schéma conformément au search_path automatiquement le cas échéant.

pg_function_is_visible(oid) restreint la sélection aux fonctions du search_path actuel. Vous pouvez ou non vouloir cela. Avec la condition pg_function_is_visible(oid) en place, la fonction est garantie d'être visible.

Si vous avez plusieurs fonctions du même nom dans plusieurs schémas ou des fonctions surchargées avec différents arguments de fonction, tous parmi ceux-ci seront listés séparément. Vous souhaiterez peut-être limiter certains schémas ou fonctions spécifiques après tout.

En relation:

Une fonction

Vous pouvez créer une fonction plpgsql autour de celle-ci pour exécuter les instructions immédiatement avec EXECUTE . Pour Postgres 9.1 ou ultérieur: Attention! Il laisse tomber vos fonctions! 

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;

Appel:

SELECT * FROM f_delfunc('my_function_name');

Ou juste:

SELECT f_delfunc('my_function_name');

De cette façon, vous n'obtenez pas la colonne nomfunc_dropped pour la colonne de résultat. Peu importe pour vous.

La fonction renvoie le nombre de fonctions trouvées et supprimées (aucune exception n'a été déclenchée) - 0 si aucune n'a été trouvée.

Cela suppose un (code par défaut) search_pathpg_catalog n'a pas été déplacé.
Plus dans ces réponses connexes:

Pour les versions de Postgres antérieures à la version 9.1 ou antérieures, les variantes de la fonction utilisant regproc et pg_get_function_identity_arguments(oid) vérifient l'historique des modifications de cette réponse.

60

Amélioration de l'original answer afin de prendre en compte schema, c'est-à-dire. 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

A partir de Postgres 10, vous pouvez supprimer des fonctions par nom uniquement, à condition que les noms soient uniques dans leur schéma. Placez simplement la déclaration suivante en haut de votre fichier de fonction:

drop function if exists my_func;

Documentation ici .

1
Mark McKelvy

Voici la requête que j'ai construite sur la solution @ Сухой27 qui génère des instructions SQL permettant de supprimer toutes les fonctions stockées dans un schéma:

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

Version légèrement améliorée de la réponse d'Erwin. Prend également en charge le suivi

  • 'like' au lieu de la correspondance exacte du nom de la fonction
  • peut fonctionner en "mode sec" et "tracer" le code SQL pour la suppression des fonctions

Code pour copier/coller:

/**
 * 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');
0
Xtra Coder