]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Revise cost_qual_eval() to compute both startup (one-time) and per-tuple
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 12 Jan 2003 22:35:29 +0000 (22:35 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 12 Jan 2003 22:35:29 +0000 (22:35 +0000)
costs for expression evaluation, not only per-tuple cost as before.
This extension is needed in order to deal realistically with hashed or
materialized sub-selects.

doc/src/sgml/indexcost.sgml
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/relnode.c
src/backend/utils/adt/selfuncs.c
src/include/nodes/relation.h
src/include/optimizer/cost.h

index 63cf8daf22deac9f2a0af094b1f6c65b35621827..2e62280a8be8e37fd117679a7ba9f5674e76e9a9 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.12 2002/08/22 00:01:40 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.13 2003/01/12 22:35:29 tgl Exp $
 -->
 
  <chapter id="indexcost">
@@ -237,9 +237,10 @@ amcostestimate (Query *root,
      * Also, we charge for evaluation of the indexquals at each index tuple.
      * All the costs are assumed to be paid incrementally during the scan.
      */
-    *indexStartupCost = 0;
+    cost_qual_eval(&index_qual_cost, indexQuals);
+    *indexStartupCost = index_qual_cost.startup;
     *indexTotalCost = numIndexPages +
-        (cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
+        (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
      </programlisting>
     </para>
    </step>
index 29b23948dfe45a6c29e8c4413728611c616ecce9..1d736b34b91102c615c3924a58e3b13148e732d7 100644 (file)
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.98 2002/12/30 15:21:21 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.99 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,7 +87,7 @@ bool          enable_hashjoin = true;
 
 static Selectivity estimate_hash_bucketsize(Query *root, Var *var,
                                                                                        int nbuckets);
-static bool cost_qual_eval_walker(Node *node, Cost *total);
+static bool cost_qual_eval_walker(Node *node, QualCost *total);
 static Selectivity approx_selectivity(Query *root, List *quals);
 static void set_rel_width(Query *root, RelOptInfo *rel);
 static double relation_byte_size(double tuples, int width);
@@ -131,7 +131,8 @@ cost_seqscan(Path *path, Query *root,
        run_cost += baserel->pages; /* sequential fetches with cost 1.0 */
 
        /* CPU costs */
-       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
+       startup_cost += baserel->baserestrictcost.startup;
+       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
        run_cost += cpu_per_tuple * baserel->tuples;
 
        path->startup_cost = startup_cost;
@@ -344,17 +345,25 @@ cost_index(Path *path, Query *root,
         *
         * Normally the indexquals will be removed from the list of restriction
         * clauses that we have to evaluate as qpquals, so we should subtract
-        * their costs from baserestrictcost.  XXX For a lossy index, not all
-        * the quals will be removed and so we really shouldn't subtract their
-        * costs; but detecting that seems more expensive than it's worth.
-        * Also, if we are doing a join then 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.
+        * their costs from baserestrictcost.  But if we are doing a join then
+        * 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 For a lossy index, not all the quals will be removed and so we
+        * really shouldn't subtract their costs; but detecting that seems more
+        * expensive than it's worth.
         */
-       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
+       startup_cost += baserel->baserestrictcost.startup;
+       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
 
        if (!is_injoin)
-               cpu_per_tuple -= cost_qual_eval(indexQuals);
+       {
+               QualCost        index_qual_cost;
+
+               cost_qual_eval(&index_qual_cost, indexQuals);
+               cpu_per_tuple -= index_qual_cost.per_tuple;
+       }
 
        run_cost += cpu_per_tuple * tuples_fetched;
 
@@ -386,7 +395,8 @@ cost_tidscan(Path *path, Query *root,
        run_cost += random_page_cost * ntuples;
 
        /* CPU costs */
-       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
+       startup_cost += baserel->baserestrictcost.startup;
+       cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
        run_cost += cpu_per_tuple * ntuples;
 
        path->startup_cost = startup_cost;
@@ -416,7 +426,8 @@ cost_functionscan(Path *path, Query *root, RelOptInfo *baserel)
        cpu_per_tuple = cpu_operator_cost;
 
        /* Add scanning CPU costs */
-       cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost;
+       startup_cost += baserel->baserestrictcost.startup;
+       cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
        run_cost += cpu_per_tuple * baserel->tuples;
 
        path->startup_cost = startup_cost;
@@ -656,6 +667,7 @@ cost_nestloop(Path *path, Query *root,
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        Cost            cpu_per_tuple;
+       QualCost        restrict_qual_cost;
        double          ntuples;
 
        if (!enable_nestloop)
@@ -703,7 +715,9 @@ cost_nestloop(Path *path, Query *root,
        ntuples *= outer_path->parent->rows;
 
        /* CPU costs */
-       cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
+       cost_qual_eval(&restrict_qual_cost, restrictlist);
+       startup_cost += restrict_qual_cost.startup;
+       cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
        run_cost += cpu_per_tuple * ntuples;
 
        path->startup_cost = startup_cost;
@@ -736,6 +750,7 @@ cost_mergejoin(Path *path, Query *root,
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        Cost            cpu_per_tuple;
+       QualCost        restrict_qual_cost;
        RestrictInfo *firstclause;
        Var                *leftvar;
        double          outer_rows,
@@ -850,7 +865,9 @@ cost_mergejoin(Path *path, Query *root,
                outer_path->parent->rows * inner_path->parent->rows;
 
        /* CPU costs */
-       cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
+       cost_qual_eval(&restrict_qual_cost, restrictlist);
+       startup_cost += restrict_qual_cost.startup;
+       cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
        run_cost += cpu_per_tuple * ntuples;
 
        path->startup_cost = startup_cost;
@@ -878,6 +895,7 @@ cost_hashjoin(Path *path, Query *root,
        Cost            startup_cost = 0;
        Cost            run_cost = 0;
        Cost            cpu_per_tuple;
+       QualCost        restrict_qual_cost;
        double          ntuples;
        double          outerbytes = relation_byte_size(outer_path->parent->rows,
                                                                                          outer_path->parent->width);
@@ -984,7 +1002,9 @@ cost_hashjoin(Path *path, Query *root,
                outer_path->parent->rows * inner_path->parent->rows;
 
        /* CPU costs */
-       cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
+       cost_qual_eval(&restrict_qual_cost, restrictlist);
+       startup_cost += restrict_qual_cost.startup;
+       cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
        run_cost += cpu_per_tuple * ntuples;
 
        /*
@@ -1185,16 +1205,20 @@ estimate_hash_bucketsize(Query *root, Var *var, int nbuckets)
 
 /*
  * cost_qual_eval
- *             Estimate the CPU cost of evaluating a WHERE clause (once).
+ *             Estimate the CPU costs of evaluating a WHERE clause.
  *             The input can be either an implicitly-ANDed list of boolean
  *             expressions, or a list of RestrictInfo nodes.
+ *             The result includes both a one-time (startup) component,
+ *             and a per-evaluation component.
  */
-Cost
-cost_qual_eval(List *quals)
+void
+cost_qual_eval(QualCost *cost, List *quals)
 {
-       Cost            total = 0;
        List       *l;
 
+       cost->startup = 0;
+       cost->per_tuple = 0;
+
        /* We don't charge any cost for the implicit ANDing at top level ... */
 
        foreach(l, quals)
@@ -1205,31 +1229,32 @@ cost_qual_eval(List *quals)
                 * RestrictInfo nodes contain an eval_cost field reserved for this
                 * routine's use, so that it's not necessary to evaluate the qual
                 * clause's cost more than once.  If the clause's cost hasn't been
-                * computed yet, the field will contain -1.
+                * computed yet, the field's startup value will contain -1.
                 */
                if (qual && IsA(qual, RestrictInfo))
                {
                        RestrictInfo *restrictinfo = (RestrictInfo *) qual;
 
-                       if (restrictinfo->eval_cost < 0)
+                       if (restrictinfo->eval_cost.startup < 0)
                        {
-                               restrictinfo->eval_cost = 0;
+                               restrictinfo->eval_cost.startup = 0;
+                               restrictinfo->eval_cost.per_tuple = 0;
                                cost_qual_eval_walker((Node *) restrictinfo->clause,
                                                                          &restrictinfo->eval_cost);
                        }
-                       total += restrictinfo->eval_cost;
+                       cost->startup += restrictinfo->eval_cost.startup;
+                       cost->per_tuple += restrictinfo->eval_cost.per_tuple;
                }
                else
                {
                        /* If it's a bare expression, must always do it the hard way */
-                       cost_qual_eval_walker(qual, &total);
+                       cost_qual_eval_walker(qual, cost);
                }
        }
-       return total;
 }
 
 static bool
-cost_qual_eval_walker(Node *node, Cost *total)
+cost_qual_eval_walker(Node *node, QualCost *total)
 {
        if (node == NULL)
                return false;
@@ -1246,43 +1271,96 @@ cost_qual_eval_walker(Node *node, Cost *total)
        if (IsA(node, FuncExpr) ||
                IsA(node, OpExpr) ||
                IsA(node, DistinctExpr))
-               *total += cpu_operator_cost;
+       {
+               total->per_tuple += cpu_operator_cost;
+       }
+       else if (IsA(node, SubLink))
+       {
+               /* This routine should not be applied to un-planned expressions */
+               elog(ERROR, "cost_qual_eval: can't handle unplanned sub-select");
+       }
        else if (IsA(node, SubPlan))
        {
                /*
-                * A subplan node in an expression indicates that the
-                * subplan will be executed on each evaluation, so charge
-                * accordingly. (We assume that sub-selects that can be
-                * executed as InitPlans have already been removed from
-                * the expression.)
+                * A subplan node in an expression typically indicates that the
+                * subplan will be executed on each evaluation, so charge accordingly.
+                * (Sub-selects that can be executed as InitPlans have already been
+                * removed from the expression.)
+                *
+                * An exception occurs when we have decided we can implement the
+                * subplan by hashing.
                 *
-                * NOTE: this logic should agree with the estimates used by
-                * make_subplan() in plan/subselect.c.
                 */
                SubPlan    *subplan = (SubPlan *) node;
                Plan       *plan = subplan->plan;
-               Cost            subcost;
 
-               if (subplan->subLinkType == EXISTS_SUBLINK)
+               if (subplan->useHashTable)
                {
-                       /* we only need to fetch 1 tuple */
-                       subcost = plan->startup_cost +
-                               (plan->total_cost - plan->startup_cost) / plan->plan_rows;
-               }
-               else if (subplan->subLinkType == ALL_SUBLINK ||
-                                subplan->subLinkType == ANY_SUBLINK)
-               {
-                       /* assume we need 50% of the tuples */
-                       subcost = plan->startup_cost +
-                               0.50 * (plan->total_cost - plan->startup_cost);
-                       /* XXX what if subplan has been materialized? */
+                       /*
+                        * If we are using a hash table for the subquery outputs, then
+                        * the cost of evaluating the query is a one-time cost.
+                        * We charge one cpu_operator_cost per tuple for the work of
+                        * loading the hashtable, too.
+                        */
+                       total->startup += plan->total_cost +
+                               cpu_operator_cost * plan->plan_rows;
+                       /*
+                        * The per-tuple costs include the cost of evaluating the
+                        * lefthand expressions, plus the cost of probing the hashtable.
+                        * Recursion into the exprs list will handle the lefthand
+                        * expressions properly, and will count one cpu_operator_cost
+                        * for each comparison operator.  That is probably too low for
+                        * the probing cost, but it's hard to make a better estimate,
+                        * so live with it for now.
+                        */
                }
                else
                {
-                       /* assume we need all tuples */
-                       subcost = plan->total_cost;
+                       /*
+                        * Otherwise we will be rescanning the subplan output on each
+                        * evaluation.  We need to estimate how much of the output
+                        * we will actually need to scan.  NOTE: this logic should
+                        * agree with the estimates used by make_subplan() in
+                        * plan/subselect.c.
+                        */
+                       Cost    plan_run_cost = plan->total_cost - plan->startup_cost;
+
+                       if (subplan->subLinkType == EXISTS_SUBLINK)
+                       {
+                               /* we only need to fetch 1 tuple */
+                               total->per_tuple += plan_run_cost / plan->plan_rows;
+                       }
+                       else if (subplan->subLinkType == ALL_SUBLINK ||
+                                        subplan->subLinkType == ANY_SUBLINK)
+                       {
+                               /* assume we need 50% of the tuples */
+                               total->per_tuple += 0.50 * plan_run_cost;
+                               /* also charge a cpu_operator_cost per row examined */
+                               total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
+                       }
+                       else
+                       {
+                               /* assume we need all tuples */
+                               total->per_tuple += plan_run_cost;
+                       }
+                       /*
+                        * Also account for subplan's startup cost.
+                        * If the subplan is uncorrelated or undirect correlated,
+                        * AND its topmost node is a Sort or Material node, assume
+                        * that we'll only need to pay its startup cost once;
+                        * otherwise assume we pay the startup cost every time.
+                        */
+                       if (subplan->parParam == NIL &&
+                               (IsA(plan, Sort) ||
+                                IsA(plan, Material)))
+                       {
+                               total->startup += plan->startup_cost;
+                       }
+                       else
+                       {
+                               total->per_tuple += plan->startup_cost;
+                       }
                }
-               *total += subcost;
        }
 
        return expression_tree_walker(node, cost_qual_eval_walker,
@@ -1388,7 +1466,7 @@ set_baserel_size_estimates(Query *root, RelOptInfo *rel)
 
        rel->rows = temp;
 
-       rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
+       cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
 
        set_rel_width(root, rel);
 }
@@ -1533,7 +1611,7 @@ set_function_size_estimates(Query *root, RelOptInfo *rel)
 
        rel->rows = temp;
 
-       rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
+       cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
 
        set_rel_width(root, rel);
 }
index 151a37a88894cc4704cc652e67d41475276bf703..1c1a848ea72dcc8be4c7b6ec0264f0d596a0352a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.79 2002/12/17 01:18:25 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.80 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -396,7 +396,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
 
        restrictinfo->clause = (Expr *) clause;
        restrictinfo->subclauseindices = NIL;
-       restrictinfo->eval_cost = -1;           /* not computed until needed */
+       restrictinfo->eval_cost.startup = -1; /* not computed until needed */
        restrictinfo->this_selec = -1;          /* not computed until needed */
        restrictinfo->mergejoinoperator = InvalidOid;
        restrictinfo->left_sortop = InvalidOid;
index 4d5adc4d47bc45334041c8cbacb907486f3d544e..5177e210d3d66cfde830e1dd2c7f99507f81bbd8 100644 (file)
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.84 2003/01/05 00:56:40 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.85 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -853,7 +853,7 @@ adjust_inherited_attrs_mutator(Node *node,
                        adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
 
                newinfo->subclauseindices = NIL;
-               newinfo->eval_cost = -1;        /* reset these too */
+               newinfo->eval_cost.startup = -1; /* reset these too */
                newinfo->this_selec = -1;
                newinfo->left_pathkey = NIL;    /* and these */
                newinfo->right_pathkey = NIL;
index b93816bd21b857e41ac158e79e66d30afebe41fa..296d211ad12166e58dc3f8d98a13e1473c837cd7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.41 2002/11/24 21:52:14 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.42 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -149,7 +149,8 @@ make_base_rel(Query *root, int relid)
        rel->joinrti = 0;
        rel->joinrteids = NIL;
        rel->baserestrictinfo = NIL;
-       rel->baserestrictcost = 0;
+       rel->baserestrictcost.startup = 0;
+       rel->baserestrictcost.per_tuple = 0;
        rel->outerjoinset = NIL;
        rel->joininfo = NIL;
        rel->index_outer_relids = NIL;
@@ -363,7 +364,8 @@ build_join_rel(Query *root,
        joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids),
                                                                inner_rel->joinrteids);
        joinrel->baserestrictinfo = NIL;
-       joinrel->baserestrictcost = 0;
+       joinrel->baserestrictcost.startup = 0;
+       joinrel->baserestrictcost.per_tuple = 0;
        joinrel->outerjoinset = NIL;
        joinrel->joininfo = NIL;
        joinrel->index_outer_relids = NIL;
index 58c63c210cc4057ed6288a4be3b02d9fc3f5cd7f..977cbe0a5e48a7c4d43f2e401e1f3c3583c0d281 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.124 2002/12/17 01:18:35 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.125 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3757,6 +3757,7 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 {
        double          numIndexTuples;
        double          numIndexPages;
+       QualCost        index_qual_cost;
        List       *selectivityQuals = indexQuals;
 
        /*
@@ -3826,9 +3827,10 @@ genericcostestimate(Query *root, RelOptInfo *rel,
         * tuple. All the costs are assumed to be paid incrementally during
         * the scan.
         */
-       *indexStartupCost = 0;
+       cost_qual_eval(&index_qual_cost, indexQuals);
+       *indexStartupCost = index_qual_cost.startup;
        *indexTotalCost = numIndexPages +
-               (cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
+               (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
 
        /*
         * Generic assumption about index correlation: there isn't any.
index 6da47683e6e23f21e44c7875776693b4a9ece905..d3fb2231f1e99be2a4201207106158a6767d9555 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.74 2002/12/12 15:49:40 tgl Exp $
+ * $Id: relation.h,v 1.75 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,16 @@ typedef enum CostSelector
        STARTUP_COST, TOTAL_COST
 } CostSelector;
 
+/*
+ * The cost estimate produced by cost_qual_eval() includes both a one-time
+ * (startup) cost, and a per-tuple cost.
+ */
+typedef struct QualCost
+{
+       Cost            startup;                /* one-time cost */
+       Cost            per_tuple;              /* per-evaluation cost */
+} QualCost;
+
 /*----------
  * RelOptInfo
  *             Per-relation information for planning/optimization
@@ -199,7 +209,7 @@ typedef struct RelOptInfo
        /* used by various scans and joins: */
        List       *baserestrictinfo;           /* RestrictInfo structures (if
                                                                                 * base rel) */
-       Cost            baserestrictcost;               /* cost of evaluating the above */
+       QualCost        baserestrictcost;               /* cost of evaluating the above */
        Relids          outerjoinset;   /* integer list of base relids */
        List       *joininfo;           /* JoinInfo structures */
 
@@ -570,7 +580,7 @@ typedef struct RestrictInfo
        /* subclauseindices is a List of Lists of IndexOptInfos */
 
        /* cache space for costs (currently only used for join clauses) */
-       Cost            eval_cost;              /* eval cost of clause; -1 if not yet set */
+       QualCost        eval_cost;              /* eval cost of clause; -1 if not yet set */
        Selectivity this_selec;         /* selectivity; -1 if not yet set */
 
        /* valid if clause is mergejoinable, else InvalidOid: */
index 5bb5092571bc6cc6b16e122124b8bb404a5e46fb..b5fef65c5dc1630c25870da91b2e3ff00d385fdc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: cost.h,v 1.49 2002/11/30 05:21:03 tgl Exp $
+ * $Id: cost.h,v 1.50 2003/01/12 22:35:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,7 +83,7 @@ extern void cost_hashjoin(Path *path, Query *root,
                          Path *outer_path, Path *inner_path,
                          List *restrictlist,
                          List *hashclauses);
-extern Cost cost_qual_eval(List *quals);
+extern void cost_qual_eval(QualCost *cost, List *quals);
 extern void set_baserel_size_estimates(Query *root, RelOptInfo *rel);
 extern void set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
                                                   RelOptInfo *outer_rel,