Re: Slow set-returning functions

From: Dean Rasheed <dean_rasheed(at)hotmail(dot)com>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Merlin Moncure <mmoncure(at)gmail(dot)com>
Cc: Heikki Linnakangas <heikki(at)enterprisedb(dot)com>, <pgsql-performance(at)postgresql(dot)org>
Subject: Re: Slow set-returning functions
Date: 2008-01-27 17:29:34
Message-ID: BAY113-W336D429DFB9A2E2772E906F23B0@phx.gbl
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-performance


>> Is there any way that I can see what execution plan is being used
>> internally by the functions?
>>
>
> Not directly, but you can do this:
>
>
> postgres=# PREPARE p (int4) AS SELECT id FROM foo WHERE lower(name) LIKE
> 'foo' ORDER BY id OFFSET 0 LIMIT $1;
> PREPARE
>
> postgres=# EXPLAIN EXECUTE p(100); QUERY PLAN
> -----------------------------------------------------------------------------
> Limit (cost=0.00..49.18 rows=2 width=4)
>
> -> Index Scan using foo_pkey on foo (cost=0.00..614.77 rows=25 width=4)
> Filter: (lower(name) ~~ 'foo'::text)
> (3 rows)

I think that having the ability to see the execution plans being used
by queries inside functions would be quite useful.

More generally, I would like to be able to log the execution plans of
all queries issued by an application (in my case I am working on a web
application, where some of the queries are auto-generated by
Hibernate). I've tried setting debug_print_plan, but the results are a
little hard to interpret.

As an experiment, I have tried hacking around a little with the code.
This is my first foray into the source code, so I might well be
missing something, but basically I added a new configuration parameter
debug_explain_plan which causes all queries to be instrumented and
ExecutorRun() to call explain_outNode() at the end, logging the
results at level DEBUG1.

It seems to work quite well as a development aid for my web
application. It is also useful from a psql session (similar to
AUTOTRACE in Oracle):

test=# create table foo(a int primary key);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
CREATE TABLE
test=# insert into foo values (1), (2), (3), (4), (5);
INSERT 0 5
test=# set debug_explain_plan=true;
SET
test=# set client_min_messages=debug1;
SET
test=# select count(*) from foo where a>3;
DEBUG: ------------------- query plan -------------------
DETAIL: Aggregate (cost=32.45..32.46 rows=1 width=0) (actual time=0.066..0.068 rows=1 loops=1)
-> Bitmap Heap Scan on foo (cost=10.45..30.45 rows=800 width=0) (actual time=0.039..0.043 rows=2 loops=1)
Recheck Cond: (a> 3)
-> Bitmap Index Scan on foo_pkey (cost=0.00..10.25 rows=800 width=0) (actual time=0.025..0.025 rows=2 loops=1)
Index Cond: (a> 3)
Query runtime: 0.089 ms
count
-------
2
(1 row)

test=# create function foo() returns int as 'select max(a) from foo;' language sql stable;
CREATE FUNCTION
test=# select * from foo where a=foo();
DEBUG: ------------------- query plan -------------------
DETAIL: Result (cost=0.04..0.05 rows=1 width=0) (actual time=0.044..0.044 rows=1 loops=1)
InitPlan
-> Limit (cost=0.00..0.04 rows=1 width=4) (actual time=0.032..0.034 rows=1 loops=1)
-> Index Scan Backward using foo_pkey on foo (cost=0.00..84.25 rows=2400 width=4) (actual time=0.025..0.025 rows=1 loops=1)
Filter: (a IS NOT NULL)
Query runtime: 0.050 ms
CONTEXT: SQL function "foo" statement 1
DEBUG: ------------------- query plan -------------------
DETAIL: Result (cost=0.04..0.05 rows=1 width=0) (actual time=0.037..0.037 rows=1 loops=1)
InitPlan
-> Limit (cost=0.00..0.04 rows=1 width=4) (actual time=0.027..0.029 rows=1 loops=1)
-> Index Scan Backward using foo_pkey on foo (cost=0.00..84.25 rows=2400 width=4) (actual time=0.021..0.021 rows=1 loops=1)
Filter: (a IS NOT NULL)
Query runtime: 0.044 ms
CONTEXT: SQL function "foo" statement 1
DEBUG: ------------------- query plan -------------------
DETAIL: Index Scan using foo_pkey on foo (cost=0.25..8.52 rows=1 width=4) (actual time=1.638..1.642 rows=1 loops=1)
Index Cond: (a = foo())
Query runtime: 1.686 ms
a
---
5
(1 row)

(Curious that foo() is being executed twice in this case).

The CONTEXT is very useful, particularly when functions call other
functions, since it gives the call stack (presumably only for SQL and
PL/pgSQL functions). For top-level queries I would ideally like the
CONTEXT to log the SQL being executed, but I couldn't figure out how
to access that information.

Anyway, I'd be interested to know if anyone has thought about doing
anything like this before and if anyone else might find this useful.

Dean

_________________________________________________________________
Free games, great prizes - get gaming at Gamesbox.
http://www.searchgamesbox.com

In response to

Responses

Browse pgsql-performance by date

  From Date Subject
Next Message Marcin Stępnicki 2008-01-27 19:16:20 Re: Slow set-returning functions
Previous Message Heikki Linnakangas 2008-01-27 10:47:30 Re: 1 or 2 servers for large DB scenario.