čt 23. 5. 2019 v 23:38 odesílatel Alastair McKinley <
a.mckin...@analyticsengines.com> napsal:

> Hi Andrew,
>
> Thanks for your in-depth response.  I found that adding the stable
> qualifier didn't solve the issue unfortunately.  I actually encountered the
> same issue (or at least extremely similar) today and made a bit more
> progress on defining it.
>
> I have a carefully indexed and optimised query that runs in about 2
> seconds with this structure and signature.
>
>     create or replace function example_function(param1 int, param2 int)
> returns setof custom_type as
>     $$
>         with a_few_ctes ()
>         select * from big_table where col1 = param1 and col2 = param2;
>     $$ language sql stable;
>
> This function works as expected when executed from a psql client.  I am
> calling this function via another framework (Postgrest) that executes the
> function using the following pattern:
>
>     with args as (
>         select json_to_record($1) as (param1 int,param2 int)
>     ),
>     output as (
>         select *
>         from example_function(
>             param1 := (select param1 from args),
>             param2 := (select param2 from args)
>         )
>     )
>     select * from output;
>
> Running this query with the args coming from the CTE resulted in my query
> running for many tens of minutes before I gave up.  Changing the underlying
> function to use plpgsql fixes the issue and the query runs as expected.
> Both versions work as expected when called directly, but the SQL version
> does not when called with the args coming from the CTE as shown above.
>
> The new function signature is
>
>     create or replace function example_function(param1 int, param2 int)
> returns setof custom_type as
>     $$
>         with a_few_ctes ()
>         return query select * from big_table where col1 = param1 and col2
> = param2;
>     $$ language plpgsql stable;
>
> I haven't been able to check the bad query plan with auto_explain as the
> query doesn't seem to finish.
>
> So to summarise, changing a stable SQL function to a stable plpgsql
> function when called with function args from a CTE fixes a huge performance
> issue of uncertain origin.  I hope someone can offer some suggestions as
> this has really confused me.
>

SQL functions are fast when they are inlined, but when are not inlined,
then they are significantly slower than plpgsql.

I am not sure, long time I didn't see this code. If I remember well, SQL
functions doesn't cache plans - so creates and lost plans every time.



> Best regards,
>
> Alastair
> ------------------------------
> *From:* Andrew Gierth <and...@tao11.riddles.org.uk>
> *Sent:* 19 May 2019 03:48
> *To:* Alastair McKinley
> *Cc:* pgsql-general@lists.postgresql.org
> *Subject:* Re: Strange performance degregation in sql function (PG11.1)
>
> >>>>> "Alastair" == Alastair McKinley <a.mckin...@analyticsengines.com>
> writes:
>
>  Alastair> Hi all,
>
>  Alastair> I recently experienced a performance degradation in an
>  Alastair> operational system that I can't explain. I had a function
>  Alastair> wrapper for a aggregate query that was performing well using
>  Alastair> the expected indexes with the approximate structure as shown
>  Alastair> below.
>
>  Alastair> create or replace function example_function(param1 int, param2
> int) returns setof custom_type as
>  Alastair> $$
>  Alastair>     select * from big_table where col1 = param1 and col2 =
> param2;
>  Alastair> $$ language sql;
>
> This function isn't inlinable due to missing a STABLE qualifier; that's
> a pretty big issue.
>
> Without inlining, the function will be run only with generic plans,
> which means that the decision about index usage will be made without
> knowledge of the parameter values.
>
> Was your actual function inlinable? See
> https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions
>
> You can get the query plan of a non-inlined function using the
> auto_explain module (with its log_nested_statements option). The query
> plan of non-inlined function calls is not otherwise shown by EXPLAIN.
>
>  Alastair> After creating two new indexes on this table to support a
>  Alastair> different use case during a migration, this unchanged
>  Alastair> function reduced in performance by several orders of
>  Alastair> magnitude. Running the query inside the function manually on
>  Alastair> the console however worked as expected and the query plan did
>  Alastair> not appear to have changed.
>
> But when you run it manually, you'll get a custom plan, based on the
> parameter values.
>
>  Alastair> On a hunch I changed the structure of the function to the
>  Alastair> structure below and immediately the query performance
>  Alastair> returned to the expected baseline.
>
>  Alastair> create or replace function example_function(param1 int, param2
> int) returns setof custom_type as
>  Alastair> $$
>  Alastair> BEGIN
>  Alastair>     return query execute format($query$
>  Alastair>         select * from big_table where col1 = %1$L and col2 = %1$
>  Alastair>     $query$,param1,param2);
>  Alastair> END;
>  Alastair> $$ language plpgsql;
>
> Using EXECUTE in plpgsql will get you a custom plan every time (though
> you really should have used EXECUTE USING rather than interpolating the
> parameters into the query string).
>
> I suggest looking into the inlining question first.
>
> --
> Andrew (irc:RhodiumToad)
>

Reply via email to