]> granicus.if.org Git - postgresql/commitdiff
Clamp indexscan filter condition cost estimate to be not less than zero.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 12 Apr 2012 00:24:32 +0000 (20:24 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 12 Apr 2012 00:24:32 +0000 (20:24 -0400)
cost_index tries to estimate the per-tuple costs of evaluating filter
conditions (a/k/a qpquals) by subtracting the estimated cost of the
indexqual conditions from that of the baserestrictinfo conditions.  This is
correct so long as the indexquals list is a subset of the baserestrictinfo
list.  However, in the presence of derived indexable conditions it's
completely wrong, leading to bogus or even negative scan cost estimates,
as seen for example in bug #6579 from Istvan Endredy.  In practice the
problem isn't severe except in the specific case of a LIKE optimization on
a functional index containing a very expensive function.

A proper fix for this might change cost estimates by more than people would
like for stable branches, so in the back branches let's just clamp the cost
difference to be not less than zero.  That will at least prevent completely
insane behavior, while not changing the results normally.

src/backend/optimizer/path/costsize.c

index 08b6c0ecaea45e317dd6c736bf80437dc0b1d2da..fe1c182cdba59f3ee15b88cdb88dc430f39229f6 100644 (file)
@@ -388,6 +388,14 @@ cost_index(IndexPath *path, PlannerInfo *root,
         * some of the indexquals are join clauses and shouldn't be subtracted.
         * Rather than work out exactly how much to subtract, we don't subtract
         * anything.
+        *
+        * XXX actually, this calculation is almost completely bogus, because
+        * indexquals will contain derived indexable conditions which might be
+        * quite different from the "original" quals in baserestrictinfo.  We
+        * ought to determine the actual qpqual list and cost that, rather than
+        * using this shortcut.  But that's too invasive a change to consider
+        * back-patching, so for the moment we just mask the worst aspects of the
+        * problem by clamping the subtracted amount.
         */
        startup_cost += baserel->baserestrictcost.startup;
        cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
@@ -398,7 +406,8 @@ cost_index(IndexPath *path, PlannerInfo *root,
 
                cost_qual_eval(&index_qual_cost, indexQuals, root);
                /* any startup cost still has to be paid ... */
-               cpu_per_tuple -= index_qual_cost.per_tuple;
+               cpu_per_tuple -= Min(index_qual_cost.per_tuple,
+                                                        baserel->baserestrictcost.per_tuple);
        }
 
        run_cost += cpu_per_tuple * tuples_fetched;