Plano de consulta do Postgres de uma chamada de função escrita em plpgsql

19

É possível ao usar o pgadminou plsqlpara obter um porão de um plano de consulta para uma instrução SQL executado dentro de um u Ser d efined f unção (UDF) utilizando EXPLAIN. Então, como eu me aposso do plano de consulta para uma chamada específica de uma UDF? Vejo o UDF abstraído em uma única operação F()no pgadmin.

Examinei a documentação, mas não consegui encontrar nada.

Atualmente, estou retirando as instruções e executando-as manualmente. Mas isso não serve para consultas grandes.

Por exemplo, considere o UDF abaixo. Esse UDF, mesmo que tenha a capacidade de imprimir sua sequência de consultas, não funcionará com uma cópia-colar, pois possui uma tabela temporária criada local, que não existe quando você a cola e executa.

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
fonte

Respostas:

16

Você deve poder usar a explicação automática . Ligue e

SET auto_explain.log_min_duration = 0;

e você deve obter os planos em seu log para todas as instruções executadas nessa sessão.

Você também pode querer definir

SET auto_explain.log_analyze = true; mas você basicamente executará tudo o dobro - uma vez para 'real' e outra para EXPLICAR ANALISAR. Durante uma fase de teste de desempenho sem tempo, essa saída pode ser muito mais útil do que os planos EXPLAIN, pois fornece o plano que realmente aconteceu.

rfusca
fonte
4
Como o @Erwin aponta abaixo, você deve configurar o auto_explain.log_nested_statements = ON também.
Rfusca
Obrigado, isso fez o truque. é uma pena que essa funcionalidade não esteja acessível por meio de uma GUI.
Hassan Syed
@rfusca você basicamente executa tudo em dobro, onde está a prova disso? Algumas experiências que eu não mostrei esse comportamento.
Sebastian Dressler
Perceba que isso está fazendo referência a um banco de dados de 7 anos neste momento. Provavelmente não funcionará mais assim, se você não estiver vendo os mesmos resultados.
rfusca 16/01
16

Além do conselho da @ rfusca: as instruções SQL dentro das funções plpgsql são consideradas instruções aninhadas e você precisa definir o Parâmetro adicional auto_explain.log_nested_statements.

Ao contrário de outras extensões, você não precisa executar CREATE EXTENSIONpara esta. Basta carregá-lo dinamicamente em sua sessão com LOAD. Sua sessão pode ficar assim:

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

Pode produzir muita saída de log.
O manual atual sobre auto_explain.
Depesz escreveu um artigo no blog quando foi introduzido no PostgreSQL 8.4.

Erwin Brandstetter
fonte
+1 - sido tão longo, eu esqueci sobre a necessidade de definir a linha log_nested_statements
rfusca
3
Você merece o crédito por trazer a ferramenta certa de qualquer maneira.
Erwin Brandstetter
Eu tenho um banco de dados postgres no serviço gerenciado da Amazon (RDS), para o qual LOAD 'auto_explain';retorna ERROR: access to library "auto_explain" is not allowed. O que nesse caso? Eu tive algum sucesso cortando minhas funções, return query explain select …mas isso é trabalhoso e lento.
Poshest