Re: Debugging a function - what's the best way to do this quickly? - Mailing list pgsql-general

From Merlin Moncure
Subject Re: Debugging a function - what's the best way to do this quickly?
Date
Msg-id CAHyXU0x_sonNbSyBv6mYiYGeAGPbAQpsas_z6XoMQAGrc80Q-Q@mail.gmail.com
Whole thread Raw
In response to Debugging a function - what's the best way to do this quickly?  (Kevin Burke <kev@inburke.com>)
List pgsql-general
On Tue, Dec 19, 2017 at 1:24 PM, Kevin Burke <kev@inburke.com> wrote:
> I'm writing a function that looks a little like this:
>
> DROP FUNCTION IF EXISTS myfunction;
> CREATE OR REPLACE FUNCTION myfunction(arg1 uuid,
>     _symbol text,
>     _start timestamp with time zone,
>     _end timestamp with time zone
> ) RETURNS TABLE (arg5 date, arg6 float)
> AS $$
> WITH cte1 AS ( ... ),
> cte2 AS ( ... ),
> cte3 AS ( ... ),
> cte4 AS ( ... ),
> cte5 AS ( ... )
> SELECT X as arg5, Y as arg6 FROM cte5;
> $$
>
> The function is not returning the correct results; I think the problem is in
> cte2 or cte3. What's the easiest way to debug this? I would like to send
> some test inputs through the program, observe the output from cte3, and
> modify the values and see if I get the correct new answers. Here are the
> approaches I know right now:
>
> - Modify the function return to contain the columns for cte3. (I don't think
> there is a way to indicate RETURNS * or similar wildcard)
> - Reload the function.
> - Call the function with the test arguments, and view the resulting table.
> Modify/reload/rerun as appropriate.
>
> Or:
>
> - Copy the function to another file.
> - Delete the function prologue and epilogue
> - Replace every use of the input arguments with the hardcoded values I want
> to test with
> - Run the file, making changes as necessary.
>
> This seems pretty cumbersome. Is there an easier way I am missing?
> Specifically it would be neat if it was easier to visualize the intermediate
> steps in the query production. If there are professional tools that help
> with this I would appreciate pointers to those as well.

If you have a lot of chained CTEs and the problem lies within that
chain, copying the query and subbing arguments is likely the best
option.  For really nasty situations I tend to convert the CTEs, one
at a time, to temp tables, reviewing the results on each step.  I've
scaled back my use of CTEs a lot lately for this and other reasons
(mainly problems with statistics) although I really appreciate the
lack of catalog bloat.

I also heavily abuse 'RAISE NOTICE' for debugging purposes.  Something
like this:

CREATE OR REPLACE FUNCTION Notice(TEXT) RETURNS VOID AS
$$
BEGIN
  RAISE NOTICE '%', $1;
END;
$$ LANGUAGE PLPGSQL;

CREATE OR REPLACE FUNCTION NoticeValue(anyelement) RETURNS anyelement AS
$$
  SELECT $1 FROM (SELECT NOTICE($1::TEXT)) q;
$$ LANGUAGE SQL;

'NoticeValue' can be used just about anywhere, for example:

SELECT a FROM foo WHERE...
could be quickly converted to:
SELECT NoticeValue(a) AS a FROM foo WHERE....

Don't forget, we can convert records to json and 'notice' them:
SELECT a,  NoticeValue(to_json(a)) FROM foo WHERE....

Dynamic SQL (via EXECUTE) can be a real pleasure to debug (not so much
to write and review), particularly if you (securely) do your own
parameterization since you can just print out the entire query.  From
a debugging standpoint, that's as good as it gets.

Also, there is a pl/pgsql debugger.  I don't have any experience with
it, maybe somebody else can comment.  I work exclusively with psql,
and so tend to use techniques that work well there.

merlin


pgsql-general by date:

Previous
From: Laurenz Albe
Date:
Subject: Re: Enforcing users to write schemas when creating tables
Next
From: Chris Travers
Date:
Subject: Re: Re: PostgreSQL needs percentage function