sqlpostgresqlschemadynamic-sqlddl

Change schema of multiple PostgreSQL functions in one operation?


Recently I needed to move objects from PostgreSQL's default schema "public" to another schema. I found this post which shows how to move tables which was great, but I also need to move the functions.


Solution

  • Postgres 12 or newer

    ALTER ROUTINE to cover both FUNCTION and PROCEDURE (added with pg 12).

    DO
    $do$
    DECLARE
       _qry text;
    BEGIN
       SELECT INTO _qry
              string_agg(format('ALTER ROUTINE public.%I(%s) SET SCHEMA new_schema;'  -- !
                       , p.proname, pg_get_function_identity_arguments(p.oid)), E'\n')
       FROM   pg_proc p
       JOIN   pg_namespace n ON n.oid = p.pronamespace
       WHERE  nspname = 'public';
          --  more filters?
    
       RAISE NOTICE '%', _qry;  -- inspect query string before executing
       -- EXECUTE _qry;         -- un-comment to execute
    END
    $do$;
    

    The rest still applies:

    Postgres 11 or older (original answer)

    Refine the loop some more (demonstrating only the second query):

    DO
    $do$
    DECLARE
       r   record;
       _qry text = '';
    BEGIN
       FOR r IN
          SELECT p.proname, pg_get_function_identity_arguments(p.oid) AS params
          FROM   pg_proc p
          JOIN   pg_namespace n ON n.oid = p.pronamespace
          WHERE  nspname = 'public'
          --  more filters?
       LOOP
          _qry := _qry || format(E'\nALTER FUNCTION public.%I(%s) SET SCHEMA new_schema;'
                               , r.proname, r.params);
       END LOOP;
    
       RAISE NOTICE '%', _qry;  -- inspect query string before executing
       -- EXECUTE _qry;         -- un-comment to execute
    END
    $do$;
    

    Remove unneeded tables from FROM.

    concat() may be overkill, but format() simplifies the syntax.

    Better set-based alternative

    Re-casting the problem as set-based operation is more elegant. One SELECT with string_agg() does the job:

    DO
    $do$
    DECLARE
       _qry text;
    BEGIN
       SELECT INTO _qry
              string_agg(format('ALTER FUNCTION public.%I(%s) SET SCHEMA new_schema;'
                       , p.proname, pg_get_function_identity_arguments(p.oid)), E'\n')
       FROM   pg_proc p
       JOIN   pg_namespace n ON n.oid = p.pronamespace
       WHERE  nspname = 'public';
          -- and other conditions, if needed
    
       RAISE NOTICE '%', _qry;  -- inspect query string before executing
       -- EXECUTE _qry;         -- un-comment to execute
    END
    $do$;