Обсуждение: cached plans in plpgsql

Поиск
Список
Период
Сортировка

cached plans in plpgsql

От
Kuba Ouhrabka
Дата:
Hi,

is there an easy way to flush all cached query plans in pl/pgsql
functions? I've long running sessions where data are changing and the
plans become inaccurate after a while. I can imagine something like
recreating all pl/pgsql functions. I can recreate them from sql source
files but I'd prefer recreating them inside the database without
accessing files outside. I can think only of one way - reconstructing
function source code from pg_proc and EXECUTEing it. But it's not the
cleanest solution (there isn't saved the actual source code anywhere so
there could be problems with quoting etc.). Can you see any other
possibility? How do you solve this problem? [And yes, I don't want to
re-connect...]

Thanks,

Kuba




Re: cached plans in plpgsql

От
"Merlin Moncure"
Дата:
Kuba wrote:

> is there an easy way to flush all cached query plans in pl/pgsql
> functions? I've long running sessions where data are changing and the
> plans become inaccurate after a while. I can imagine something like
> recreating all pl/pgsql functions. I can recreate them from sql source
> files but I'd prefer recreating them inside the database without
> accessing files outside. I can think only of one way - reconstructing
> function source code from pg_proc and EXECUTEing it. But it's not the
> cleanest solution (there isn't saved the actual source code anywhere
so
> there could be problems with quoting etc.). Can you see any other
> possibility? How do you solve this problem? [And yes, I don't want to
> re-connect...]

Start here:
http://archives.postgresql.org/pgsql-hackers/2005-09/msg00690.php

Merlin

Re: cached plans in plpgsql

От
Kuba Ouhrabka
Дата:
 > [howto recreate plpgsql functions]
>
> Start here:
> http://archives.postgresql.org/pgsql-hackers/2005-09/msg00690.php

Great, thanks!

I slighltly modified the function - it was not working for overloaded
functions (same name, different arguments) and for functions with named
arguments. Modified version attached for anyone interested - not perfect
but works for me...

Kuba
CREATE OR REPLACE FUNCTION recompile_function(a_oid oid) RETURNS INTEGER AS $func$
DECLARE
  Par_proc TEXT;

  Var_datos RECORD;
  Var_codigo text;
  Var_args varchar;

  Var_nameArg varchar;
  Var_nameRet varchar;
  i int;
BEGIN

  SELECT proretset, prorettype, proargtypes, proargnames, prosrc, pronargs, proname
    INTO Var_datos
  FROM pg_proc
  WHERE
      oid = a_oid
  FOR UPDATE
  ;

  Par_proc := Var_datos.proname;

  SELECT typname::varchar INTO Var_nameRet FROM pg_type WHERE oid = Var_datos.prorettype;

  Var_codigo := 'CREATE OR REPLACE FUNCTION '||Par_proc||'(';

  IF Var_datos.pronargs > 0 THEN
    Var_args := '';
    FOR i IN 0..Var_datos.pronargs-1 LOOP
        SELECT typname::varchar INTO Var_nameArg FROM pg_type WHERE oid = Var_datos.proargtypes[i];

        Var_args := Var_args|| COALESCE(Var_datos.proargnames[i+1], '') || ' ' || Var_nameArg||', ';
    END LOOP;

    Var_codigo := Var_codigo||RTRIM(Var_args,', ');
  END IF;

  if Var_datos.proretset THEN
    Var_codigo := Var_codigo||') RETURNS SETOF '||Var_nameRet||' AS''';
  ELSE
    Var_codigo := Var_codigo||') RETURNS '||Var_nameRet||' AS''';
  END IF;

  Var_codigo := Var_codigo|| replace(Var_datos.prosrc,'''' , '\'''');

  Var_codigo := Var_codigo||'''LANGUAGE ''plpgsql''';

  EXECUTE(Var_codigo);

  RETURN 0;

END;
$func$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION recompile_all_functions() RETURNS INTEGER AS $func$
DECLARE
    lr_rec RECORD;
    li_x INTEGER;
BEGIN

    FOR lr_rec IN
    SELECT
        p.oid as oid
    FROM
        pg_catalog.pg_proc p
        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
        LEFT JOIN pg_language l ON l.oid = p.prolang
    WHERE
            NOT p.proisagg
        AND pg_catalog.pg_function_is_visible(p.oid)
        AND n.nspname != 'pg_catalog'
        AND NOT p.proname IN ('recompile_all_functions', 'recompile_function')
        AND l.lanname = 'plpgsql'
    LOOP
        li_x := recompile_function(lr_rec.oid);
    END LOOP;


    RETURN 0;
END;
$func$ LANGUAGE 'plpgsql';

Re: cached plans in plpgsql

От
Tom Lane
Дата:
Kuba Ouhrabka <kuba@comgate.cz> writes:
>   IF Var_datos.pronargs > 0 THEN
>     Var_args := '';
>     FOR i IN 0..Var_datos.pronargs-1 LOOP
>         SELECT typname::varchar INTO Var_nameArg FROM pg_type WHERE oid = Var_datos.proargtypes[i];

>         Var_args := Var_args|| COALESCE(Var_datos.proargnames[i+1], '') || ' ' || Var_nameArg||', ';
>     END LOOP;

This will not work at all; it makes far too many incorrect assumptions,
like proargnames always being non-null and having subscripts that match
proargtypes.  (It'll mess things up completely for anything that has OUT
arguments, too.)

It's pretty much the hard way to form a function reference anyway ---
you can just cast the function OID to regprocedure, which aside from
avoiding a lot of subtle assumptions about the catalog contents,
will deal with schema naming issues, something the above likewise
fails at.

To avoid having to reconstruct argument names/types, I'd suggest using
an ALTER FUNCTION command instead of CREATE OR REPLACE FUNCTION, maybe

    DECLARE fullproname text := a_oid::regprocedure;
    ...
    EXECUTE 'ALTER FUNCTION ' || fullproname || ' RENAME TO ' || Var_datos.proname;

            regards, tom lane

Re: cached plans in plpgsql

От
Kuba Ouhrabka
Дата:
Tom,

many thanks. Perfect advice as usual...

Corrected version attached for the archives.

Kuba

Tom Lane napsal(a):
> Kuba Ouhrabka <kuba@comgate.cz> writes:
>
>>  IF Var_datos.pronargs > 0 THEN
>>    Var_args := '';
>>    FOR i IN 0..Var_datos.pronargs-1 LOOP
>>        SELECT typname::varchar INTO Var_nameArg FROM pg_type WHERE oid = Var_datos.proargtypes[i];
>
>
>
>>        Var_args := Var_args|| COALESCE(Var_datos.proargnames[i+1], '') || ' ' || Var_nameArg||', ';
>>    END LOOP;
>
>
> This will not work at all; it makes far too many incorrect assumptions,
> like proargnames always being non-null and having subscripts that match
> proargtypes.  (It'll mess things up completely for anything that has OUT
> arguments, too.)
>
> It's pretty much the hard way to form a function reference anyway ---
> you can just cast the function OID to regprocedure, which aside from
> avoiding a lot of subtle assumptions about the catalog contents,
> will deal with schema naming issues, something the above likewise
> fails at.
>
> To avoid having to reconstruct argument names/types, I'd suggest using
> an ALTER FUNCTION command instead of CREATE OR REPLACE FUNCTION, maybe
>
>     DECLARE fullproname text := a_oid::regprocedure;
>     ...
>     EXECUTE 'ALTER FUNCTION ' || fullproname || ' RENAME TO ' || Var_datos.proname;
>
>             regards, tom lane
CREATE OR REPLACE FUNCTION recompile_all_functions() RETURNS INTEGER AS $func$
DECLARE
    lr_rec RECORD;
    li_x INTEGER;
BEGIN

    FOR lr_rec IN
    SELECT
        p.oid as oid
    FROM
        pg_catalog.pg_proc p
        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
        LEFT JOIN pg_language l ON l.oid = p.prolang
    WHERE
            NOT p.proisagg
        AND pg_catalog.pg_function_is_visible(p.oid)
        AND n.nspname != 'pg_catalog'
        AND NOT p.proname IN ('recompile_all_functions', 'recompile_function')
        AND l.lanname = 'plpgsql'
    LOOP
        li_x := recompile_function(lr_rec.oid);
    END LOOP;


    RETURN 0;
END;
$func$ LANGUAGE 'plpgsql';

CREATE OR REPLACE FUNCTION recompile_function(a_oid oid) RETURNS INTEGER AS $func$
DECLARE
  lv_name TEXT;
  lv_fullname TEXT;
BEGIN

  SELECT INTO lv_name proname FROM pg_proc
  WHERE
          oid = a_oid
  ;

  lv_fullname := a_oid::regprocedure;

  EXECUTE 'ALTER FUNCTION ' || lv_fullname || ' RENAME TO ugly_function_name';
  lv_fullname := a_oid::regprocedure;
  EXECUTE 'ALTER FUNCTION ' || lv_fullname || ' RENAME TO ' || lv_name;

  RETURN 0;

END;
$func$ LANGUAGE 'plpgsql';