Resjunk sort columns, Heikki's index-only quals patch, and bug #5000 - Mailing list pgsql-hackers

From Tom Lane
Subject Resjunk sort columns, Heikki's index-only quals patch, and bug #5000
Date
Msg-id 9957.1250956747@sss.pgh.pa.us
Whole thread Raw
Responses Re: Resjunk sort columns, Heikki's index-only quals patch, and bug #5000
List pgsql-hackers
I've been looking at bug #5000 (must be some kind of milestone), in
which the complaint was that the planner won't use an indexscan on a
functional index to satisfy an ORDER BY.  Of course it *can* do that,
it's just not being very bright about it.  Consider the following
example in the regression database, using all default settings:

regression=# create or replace function foo(int) returns int as
regression-# 'begin return $1; end' language plpgsql immutable strict     
regression-# cost 10000;
CREATE FUNCTION
regression=# create index fooi on tenk1 (foo(unique1));
CREATE INDEX
regression=# explain verbose select * from tenk1 order by foo(unique1);
                       QUERY PLAN                                                                           
 

---------------------------------------------------------------------------------------------------------------------------------------------------------------Index
Scanusing fooi on public.tenk1  (cost=0.00..251702.25 rows=10000 width=244)  Output: unique1, unique2, two, four, ten,
twenty,hundred, thousand, twothousand, fivethous, tenthous, odd, even, stringu1, stringu2, string4, foo(unique1)
 
(2 rows)

The first thing you'll notice is that the cost estimate is supposing
that the expensive function gets re-evaluated at each row.  And the
second thing you'll notice is that the cost estimate is not incorrect,
because this plan actually *is* evaluating foo(unique1) at each row.
(I am using CVS HEAD here so that you can see resjunk output columns...)
So this is hardly going to satisfy the complainant, even if the planner
chose it over the seqscan-and-sort alternative.

The reason for this behavior is that the Query representation emitted
by the parser includes the ORDER BY expressions as "resjunk" columns
in the query targetlist.  grouping_planner() faithfully includes all
such columns in the final plan's targetlist, even ones that are not
really needed because of the form of the chosen plan.  Because it always
uses the same targetlist, it also feels that it's not necessary to worry
about the evaluation cost of the targetlist while choosing the plan
--- so it settles on indexscan or seqscan before it's ever even bothered
to compute the cost of the tlist.

This isn't a horrid strategy in "normal" cases where the sort keys
are just simple columns; even if they end up not being referenced,
pulling a couple of extra datums from the heap tuples isn't all that
much extra expense.  But if you're talking about an expensive function
then it gets objectionable.

I looked into fixing the code to omit unused resjunk sort columns, and
think that it could probably be dealt with via some reasonably
straightforward changes in grouping_planner and some of its immediate
subroutines.  However, grouping_planner is already a huge pile of subtly
intertwined spaghetti, and adding yet another set of considerations to
it will make it that much harder to understand or maintain.  (If
anyone's got some ideas about refactoring it, I'm all ears.)  So I
looked around for alternatives.

It strikes me that in the cases where it wouldn't be necessary to
compute junk sort-key columns, it would be because we were scanning an
index that includes those values.  So if the plan were set up to pull
those values from the index and return them, then we'd not have to add
this extra complexity to grouping_planner --- the argument that it's not
worth it to get rid of the junk columns comes back into play.  Moreover,
such an ability would also mean that if the user *does* ask for the
sort column value as output (ie it's not resjunk), we can still satisfy
the query from the index without recomputing the expensive function.

So this is where we come to the connection to Heikki's index-only-quals
patch.  As submitted, that code is only able to use an index column in
a scan qual, it's not able to return it as part of the scan result.
This example makes it clear that that definition is missing a large
part of the potential benefit of an index value extraction capability.

To be able to do anything along that line would require some more work
in the executor and a *lot* more work in the planner, and I'm honestly
not sure what the planner part of it would look like.  The main point
at the moment is that to do something like this, we'd want the indexscan
to certainly extract the function value from the index.  Heikki's patch
views that as an optional behavior with no real penalty for failure.
I don't think that's good enough.

I'm not sure whether to go ahead with trying to fix the unused-sort-key
behavior right now, or to leave it until something gets done on the
index value extraction front.  Comments?
        regards, tom lane


pgsql-hackers by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: DELETE syntax on JOINS
Next
From: Tom Lane
Date:
Subject: Re: Another try at reducing repeated detoast work for PostGIS