Plan de requête Postgres d'une invocation de fonction écrite en plpgsql

19

Il est possible lors de l' utilisation du pgadminou plsqlpour obtenir une prise d'un plan de requête pour une instruction SQL exécutée à l' intérieur d' un u ser d efined f Onction (UDF) à l' aide EXPLAIN. Alors, comment puis-je obtenir le plan de requête pour une invocation particulière d'un UDF? Je vois l'UDF résumé dans une seule opération F()dans pgadmin.

J'ai regardé la documentation mais je n'ai rien trouvé.

Actuellement, je retire les instructions et les exécute manuellement. Mais cela ne va pas le couper pour les grandes requêtes.

Par exemple, considérez l'UDF ci-dessous. Cette UDF, même si elle a la possibilité d'imprimer sa chaîne de requête, ne fonctionnera pas avec un copier-coller car elle a une table temporaire créée localement, qui n'existe pas lorsque vous la collez et l'exécutez.

CREATE OR REPLACE FUNCTION get_paginated_search_results(
    forum_id_ INTEGER,
    query_    CHARACTER VARYING,
    from_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    to_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    in_categories_ INTEGER[] DEFAULT '{}')
RETURNS SETOF post_result_entry AS $$
DECLARE
    join_string CHARACTER VARYING := ' ';
    from_where_date CHARACTER VARYING := ' ';
    to_where_date CHARACTER VARYING := ' ';
    query_string_ CHARACTER VARYING := ' ';
BEGIN
    IF NOT from_date_ IS NULL THEN
        from_where_date := ' AND fp.posted_at > ''' || from_date_ || '''';
    END IF;

    IF NOT to_date_ IS NULL THEN
        to_where_date := ' AND fp.posted_at < ''' || to_date_ || '''';
    END IF;

    CREATE LOCAL TEMP TABLE un_cat(id) ON COMMIT DROP AS (select * from unnest(in_categories_)) ;

    if in_categories_ != '{}' THEN
        join_string := ' INNER JOIN forum_topics ft ON fp.topic_id = ft.id ' ||
        ' INNER JOIN un_cat uc ON uc.id = ft.category_id ' ;
    END IF;

    query_string_ := '
    SELECT index,posted_at,post_text,name,join_date,quotes
    FROM forum_posts fp
    INNER JOIN forum_user fu ON
    fu.forum_id = fp.forum_id AND fu.id = fp.user_id' ||
        join_string
    ||
    'WHERE fu.forum_id = ' || forum_id_ || ' AND
    to_tsvector(''english'',fp.post_text) @@ to_tsquery(''english'','''|| query_||''')' || 
        from_where_date || 
        to_where_date
    ||';';

    RAISE NOTICE '%', query_string_ ;

    RETURN QUERY
    EXECUTE query_string_;
END;
$$ LANGUAGE plpgsql;
Hassan Syed
la source

Réponses:

16

Vous devriez pouvoir utiliser l'explication automatique . Allumez-le et

SET auto_explain.log_min_duration = 0;

et vous devriez obtenir les plans dans votre journal pour toutes les instructions exécutées dans cette session.

Vous pouvez également définir

SET auto_explain.log_analyze = true; mais vous exécuterez essentiellement tout en double - une fois pour «réel» et une fois pour EXPLIQUER ANALYSER. Pendant une phase de test de performances sans synchronisation, cette sortie peut être beaucoup plus utile que les plans EXPLAIN seuls, car elle fournit le plan qui s'est réellement passé.

rfusca
la source
4
Comme @Erwin le souligne ci-dessous, vous devez également définir auto_explain.log_nested_statements = ON.
rfusca
Merci, ça fait l'affaire. c'est dommage que cette fonctionnalité ne soit pas accessible via une interface graphique.
Hassan Syed
@rfusca, vous exécuterez essentiellement tout en double, où en est la preuve? Certaines expériences que je n'ai pas montrées ne montrent pas ce comportement.
Sebastian Dressler
Sachez que cela fait référence à une base de données de 7 ans à ce stade. Cela ne fonctionnera probablement plus comme ça si vous ne voyez pas les mêmes résultats.
rfusca
16

J'ajoute aux conseils de @ rfusca: les instructions SQL à l'intérieur des fonctions plpgsql sont considérées comme des instructions imbriquées et vous devez définir le paramètre supplémentaire auto_explain.log_nested_statements.

Contrairement à certaines autres extensions, vous n'avez pas besoin de courir CREATE EXTENSIONpour celle-ci. Il suffit de le charger dynamiquement dans votre session avec LOAD. Votre session pourrait ressembler à ceci:

LOAD 'auto_explain';
SET auto_explain.log_min_duration = 1; -- exclude very fast trivial queries
SET auto_explain.log_nested_statements = ON; -- statements inside functions
-- SET auto_explain.log_analyze = ON; -- get actual times, too
SELECT * FROM get_paginated_search_results(...);

Peut produire beaucoup de sortie de journal.
Le manuel actuel sur auto_explain.
Depesz a écrit un article de blog à ce sujet lors de son introduction avec PostgreSQL 8.4.

Erwin Brandstetter
la source
+1 - cela fait si longtemps que j'ai oublié de définir la ligne
log_nested_statements
3
Vous méritez tout de même le mérite d'avoir proposé le bon outil.
Erwin Brandstetter
J'ai une base de données postgres sur le service géré d'Amazon (RDS), pour laquelle LOAD 'auto_explain';revient ERROR: access to library "auto_explain" is not allowed. Quoi dans ce cas? J'ai réussi à pirater mes fonctions, return query explain select …mais c'est laborieux et lent.
Poshest