*-------------------------------------------------------------------------
  */
 
+/*
+ * deconstruct_indexquals is a simple function to examine the indexquals
+ * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+ * structs, one per qual expression.
+ */
+typedef struct
+{
+   RestrictInfo *rinfo;        /* the indexqual itself */
+   int         indexcol;       /* zero-based index column number */
+   bool        varonleft;      /* true if index column is on left of qual */
+   Oid         clause_op;      /* qual's operator OID, if relevant */
+   Node       *other_operand;  /* non-index operand of qual's operator */
+} IndexQualInfo;
+
+static List *
+deconstruct_indexquals(IndexPath *path)
+{
+   List       *result = NIL;
+   IndexOptInfo *index = path->indexinfo;
+   ListCell   *lcc,
+              *lci;
+
+   forboth(lcc, path->indexquals, lci, path->indexqualcols)
+   {
+       RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
+       int         indexcol = lfirst_int(lci);
+       Expr       *clause;
+       Node       *leftop,
+                  *rightop;
+       IndexQualInfo *qinfo;
+
+       Assert(IsA(rinfo, RestrictInfo));
+       clause = rinfo->clause;
+
+       qinfo = (IndexQualInfo *) palloc(sizeof(IndexQualInfo));
+       qinfo->rinfo = rinfo;
+       qinfo->indexcol = indexcol;
+
+       if (IsA(clause, OpExpr))
+       {
+           qinfo->clause_op = ((OpExpr *) clause)->opno;
+           leftop = get_leftop(clause);
+           rightop = get_rightop(clause);
+           if (match_index_to_operand(leftop, indexcol, index))
+           {
+               qinfo->varonleft = true;
+               qinfo->other_operand = rightop;
+           }
+           else
+           {
+               Assert(match_index_to_operand(rightop, indexcol, index));
+               qinfo->varonleft = false;
+               qinfo->other_operand = leftop;
+           }
+       }
+       else if (IsA(clause, RowCompareExpr))
+       {
+           RowCompareExpr *rc = (RowCompareExpr *) clause;
+
+           qinfo->clause_op = linitial_oid(rc->opnos);
+           /* Examine only first columns to determine left/right sides */
+           if (match_index_to_operand((Node *) linitial(rc->largs),
+                                      indexcol, index))
+           {
+               qinfo->varonleft = true;
+               qinfo->other_operand = (Node *) rc->rargs;
+           }
+           else
+           {
+               Assert(match_index_to_operand((Node *) linitial(rc->rargs),
+                                             indexcol, index));
+               qinfo->varonleft = false;
+               qinfo->other_operand = (Node *) rc->largs;
+           }
+       }
+       else if (IsA(clause, ScalarArrayOpExpr))
+       {
+           ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+           qinfo->clause_op = saop->opno;
+           /* index column is always on the left in this case */
+           Assert(match_index_to_operand((Node *) linitial(saop->args),
+                                         indexcol, index));
+           qinfo->varonleft = true;
+           qinfo->other_operand = (Node *) lsecond(saop->args);
+       }
+       else if (IsA(clause, NullTest))
+       {
+           NullTest   *nt = (NullTest *) clause;
+
+           qinfo->clause_op = InvalidOid;
+           Assert(match_index_to_operand((Node *) nt->arg, indexcol, index));
+           qinfo->varonleft = true;
+           qinfo->other_operand = NULL;
+       }
+       else
+       {
+           elog(ERROR, "unsupported indexqual type: %d",
+                (int) nodeTag(clause));
+       }
+
+       result = lappend(result, qinfo);
+   }
+   return result;
+}
+
+/*
+ * Simple function to compute the total eval cost of the "other operands"
+ * in an IndexQualInfo list.  Since we know these will be evaluated just
+ * once per scan, there's no need to distinguish startup from per-row cost.
+ */
+static Cost
+other_operands_eval_cost(PlannerInfo *root, List *qinfos)
+{
+   Cost        qual_arg_cost = 0;
+   ListCell   *lc;
+
+   foreach(lc, qinfos)
+   {
+       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
+       QualCost    index_qual_cost;
+
+       cost_qual_eval_node(&index_qual_cost, qinfo->other_operand, root);
+       qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+   }
+   return qual_arg_cost;
+}
+
+/*
+ * Get other-operand eval cost for an index orderby list.
+ *
+ * Index orderby expressions aren't represented as RestrictInfos (since they
+ * aren't boolean, usually).  So we can't apply deconstruct_indexquals to
+ * them.  However, they are much simpler to deal with since they are always
+ * OpExprs and the index column is always on the left.
+ */
+static Cost
+orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
+{
+   Cost        qual_arg_cost = 0;
+   ListCell   *lc;
+
+   foreach(lc, path->indexorderbys)
+   {
+       Expr       *clause = (Expr *) lfirst(lc);
+       Node       *other_operand;
+       QualCost    index_qual_cost;
+
+       if (IsA(clause, OpExpr))
+       {
+           other_operand = get_rightop(clause);
+       }
+       else
+       {
+           elog(ERROR, "unsupported indexorderby type: %d",
+                (int) nodeTag(clause));
+           other_operand = NULL;       /* keep compiler quiet */
+       }
+
+       cost_qual_eval_node(&index_qual_cost, other_operand, root);
+       qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+   }
+   return qual_arg_cost;
+}
+
 /*
  * genericcostestimate is a general-purpose estimator that can be used for
  * most index types.  In some cases we use genericcostestimate as the base
 genericcostestimate(PlannerInfo *root,
                    IndexPath *path,
                    double loop_count,
+                   List *qinfos,
                    GenericCosts *costs)
 {
    IndexOptInfo *index = path->indexinfo;
    double      num_sa_scans;
    double      num_outer_scans;
    double      num_scans;
-   QualCost    index_qual_cost;
    double      qual_op_cost;
    double      qual_arg_cost;
    List       *selectivityQuals;
     * Detecting that that might be needed seems more expensive than it's
     * worth, though, considering all the other inaccuracies here ...
     */
-   cost_qual_eval(&index_qual_cost, indexQuals, root);
-   qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple;
-   cost_qual_eval(&index_qual_cost, indexOrderBys, root);
-   qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+   qual_arg_cost = other_operands_eval_cost(root, qinfos) +
+       orderby_operands_eval_cost(root, path);
    qual_op_cost = cpu_operator_cost *
        (list_length(indexQuals) + list_length(indexOrderBys));
-   qual_arg_cost -= qual_op_cost;
-   if (qual_arg_cost < 0)      /* just in case... */
-       qual_arg_cost = 0;
 
    indexStartupCost = qual_arg_cost;
    indexTotalCost += qual_arg_cost;
    Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
    double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
    IndexOptInfo *index = path->indexinfo;
+   List       *qinfos;
    GenericCosts costs;
    Oid         relid;
    AttrNumber  colnum;
    bool        found_saop;
    bool        found_is_null_op;
    double      num_sa_scans;
-   ListCell   *lcc,
-              *lci;
+   ListCell   *lc;
+
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
 
    /*
     * For a btree scan, only leading '=' quals plus inequality quals for the
    found_saop = false;
    found_is_null_op = false;
    num_sa_scans = 1;
-   forboth(lcc, path->indexquals, lci, path->indexqualcols)
+   foreach(lc, qinfos)
    {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
-       Expr       *clause;
-       Node       *leftop,
-                  *rightop PG_USED_FOR_ASSERTS_ONLY;
+       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
+       RestrictInfo *rinfo = qinfo->rinfo;
+       Expr       *clause = rinfo->clause;
        Oid         clause_op;
        int         op_strategy;
-       bool        is_null_op = false;
 
-       if (indexcol != lfirst_int(lci))
+       if (indexcol != qinfo->indexcol)
        {
            /* Beginning of a new column's quals */
            if (!eqQualHere)
                break;          /* done if no '=' qual for indexcol */
            eqQualHere = false;
            indexcol++;
-           if (indexcol != lfirst_int(lci))
+           if (indexcol != qinfo->indexcol)
                break;          /* no quals at all for indexcol */
        }
 
-       Assert(IsA(rinfo, RestrictInfo));
-       clause = rinfo->clause;
-
-       if (IsA(clause, OpExpr))
-       {
-           leftop = get_leftop(clause);
-           rightop = get_rightop(clause);
-           clause_op = ((OpExpr *) clause)->opno;
-       }
-       else if (IsA(clause, RowCompareExpr))
-       {
-           RowCompareExpr *rc = (RowCompareExpr *) clause;
-
-           leftop = (Node *) linitial(rc->largs);
-           rightop = (Node *) linitial(rc->rargs);
-           clause_op = linitial_oid(rc->opnos);
-       }
-       else if (IsA(clause, ScalarArrayOpExpr))
+       if (IsA(clause, ScalarArrayOpExpr))
        {
-           ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+           int         alength = estimate_array_length(qinfo->other_operand);
 
-           leftop = (Node *) linitial(saop->args);
-           rightop = (Node *) lsecond(saop->args);
-           clause_op = saop->opno;
            found_saop = true;
+           /* count up number of SA scans induced by indexBoundQuals only */
+           if (alength > 1)
+               num_sa_scans *= alength;
        }
        else if (IsA(clause, NullTest))
        {
            NullTest   *nt = (NullTest *) clause;
 
-           leftop = (Node *) nt->arg;
-           rightop = NULL;
-           clause_op = InvalidOid;
            if (nt->nulltesttype == IS_NULL)
            {
                found_is_null_op = true;
-               is_null_op = true;
+               /* IS NULL is like = for selectivity determination purposes */
+               eqQualHere = true;
            }
        }
-       else
-       {
-           elog(ERROR, "unsupported indexqual type: %d",
-                (int) nodeTag(clause));
-           continue;           /* keep compiler quiet */
-       }
 
-       if (match_index_to_operand(leftop, indexcol, index))
-       {
-           /* clause_op is correct */
-       }
-       else
-       {
-           Assert(match_index_to_operand(rightop, indexcol, index));
-           /* Must flip operator to get the opfamily member */
-           clause_op = get_commutator(clause_op);
-       }
+       /*
+        * We would need to commute the clause_op if not varonleft, except
+        * that we only care if it's equality or not, so that refinement is
+        * unnecessary.
+        */
+       clause_op = qinfo->clause_op;
 
        /* check for equality operator */
        if (OidIsValid(clause_op))
            if (op_strategy == BTEqualStrategyNumber)
                eqQualHere = true;
        }
-       else if (is_null_op)
-       {
-           /* IS NULL is like = for purposes of selectivity determination */
-           eqQualHere = true;
-       }
-       /* count up number of SA scans induced by indexBoundQuals only */
-       if (IsA(clause, ScalarArrayOpExpr))
-       {
-           ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
-           int         alength = estimate_array_length(lsecond(saop->args));
 
-           if (alength > 1)
-               num_sa_scans *= alength;
-       }
        indexBoundQuals = lappend(indexBoundQuals, rinfo);
    }
 
    MemSet(&costs, 0, sizeof(costs));
    costs.numIndexTuples = numIndexTuples;
 
-   genericcostestimate(root, path, loop_count, &costs);
+   genericcostestimate(root, path, loop_count, qinfos, &costs);
 
    /*
     * Add a CPU-cost component to represent the costs of initial btree
    Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
    Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
    double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
+   List       *qinfos;
    GenericCosts costs;
 
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
+
    MemSet(&costs, 0, sizeof(costs));
 
-   genericcostestimate(root, path, loop_count, &costs);
+   genericcostestimate(root, path, loop_count, qinfos, &costs);
 
    /*
     * A hash index has no descent costs as such, since the index AM can go
    Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
    double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
    IndexOptInfo *index = path->indexinfo;
+   List       *qinfos;
    GenericCosts costs;
    Cost        descentCost;
 
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
+
    MemSet(&costs, 0, sizeof(costs));
 
-   genericcostestimate(root, path, loop_count, &costs);
+   genericcostestimate(root, path, loop_count, qinfos, &costs);
 
    /*
     * We model index descent costs similarly to those for btree, but to do
    Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
    double     *indexCorrelation = (double *) PG_GETARG_POINTER(6);
    IndexOptInfo *index = path->indexinfo;
+   List       *qinfos;
    GenericCosts costs;
    Cost        descentCost;
 
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
+
    MemSet(&costs, 0, sizeof(costs));
 
-   genericcostestimate(root, path, loop_count, &costs);
+   genericcostestimate(root, path, loop_count, qinfos, &costs);
 
    /*
     * We model index descent costs similarly to those for btree, but to do
    double      arrayScans;
 } GinQualCounts;
 
-/* Find the index column matching "op"; return its index, or -1 if no match */
-static int
-find_index_column(Node *op, IndexOptInfo *index)
-{
-   int         i;
-
-   for (i = 0; i < index->ncolumns; i++)
-   {
-       if (match_index_to_operand(op, i, index))
-           return i;
-   }
-
-   return -1;
-}
-
 /*
  * Estimate the number of index terms that need to be searched for while
  * testing the given GIN query, and increment the counts in *counts
  * appropriately.  If the query is unsatisfiable, return false.
  */
 static bool
-gincost_opexpr(PlannerInfo *root, IndexOptInfo *index, OpExpr *clause,
+gincost_opexpr(PlannerInfo *root,
+              IndexOptInfo *index,
+              IndexQualInfo *qinfo,
               GinQualCounts *counts)
 {
-   Node       *leftop = get_leftop((Expr *) clause);
-   Node       *rightop = get_rightop((Expr *) clause);
-   Oid         clause_op = clause->opno;
-   int         indexcol;
-   Node       *operand;
+   int         indexcol = qinfo->indexcol;
+   Oid         clause_op = qinfo->clause_op;
+   Node       *operand = qinfo->other_operand;
 
-   /* Locate the operand being compared to the index column */
-   if ((indexcol = find_index_column(leftop, index)) >= 0)
+   if (!qinfo->varonleft)
    {
-       operand = rightop;
-   }
-   else if ((indexcol = find_index_column(rightop, index)) >= 0)
-   {
-       operand = leftop;
+       /* must commute the operator */
        clause_op = get_commutator(clause_op);
    }
-   else
-   {
-       elog(ERROR, "could not match index to operand");
-       operand = NULL;         /* keep compiler quiet */
-   }
 
    /* aggressively reduce to a constant, and look through relabeling */
    operand = estimate_expression_value(root, operand);
  */
 static bool
 gincost_scalararrayopexpr(PlannerInfo *root,
-                         IndexOptInfo *index, ScalarArrayOpExpr *clause,
+                         IndexOptInfo *index,
+                         IndexQualInfo *qinfo,
                          double numIndexEntries,
                          GinQualCounts *counts)
 {
-   Node       *leftop = (Node *) linitial(clause->args);
-   Node       *rightop = (Node *) lsecond(clause->args);
-   Oid         clause_op = clause->opno;
-   int         indexcol;
+   int         indexcol = qinfo->indexcol;
+   Oid         clause_op = qinfo->clause_op;
+   Node       *rightop = qinfo->other_operand;
    ArrayType  *arrayval;
    int16       elmlen;
    bool        elmbyval;
    int         numPossible = 0;
    int         i;
 
-   Assert(clause->useOr);
-
-   /* index column must be on the left */
-   if ((indexcol = find_index_column(leftop, index)) < 0)
-       elog(ERROR, "could not match index to operand");
+   Assert(((ScalarArrayOpExpr *) qinfo->rinfo->clause)->useOr);
 
    /* aggressively reduce to a constant, and look through relabeling */
    rightop = estimate_expression_value(root, rightop);
    IndexOptInfo *index = path->indexinfo;
    List       *indexQuals = path->indexquals;
    List       *indexOrderBys = path->indexorderbys;
+   List       *qinfos;
    ListCell   *l;
    List       *selectivityQuals;
    double      numPages = index->pages,
                qual_arg_cost,
                spc_random_page_cost,
                outer_scans;
-   QualCost    index_qual_cost;
    Relation    indexRel;
    GinStatsData ginStats;
 
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
+
    /*
     * Obtain statistic information from the meta page
     */
    counts.arrayScans = 1;
    matchPossible = true;
 
-   foreach(l, indexQuals)
+   foreach(l, qinfos)
    {
-       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
-       Expr       *clause;
+       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
+       Expr       *clause = qinfo->rinfo->clause;
 
-       Assert(IsA(rinfo, RestrictInfo));
-       clause = rinfo->clause;
        if (IsA(clause, OpExpr))
        {
            matchPossible = gincost_opexpr(root,
                                           index,
-                                          (OpExpr *) clause,
+                                          qinfo,
                                           &counts);
            if (!matchPossible)
                break;
        {
            matchPossible = gincost_scalararrayopexpr(root,
                                                      index,
-                                               (ScalarArrayOpExpr *) clause,
+                                                     qinfo,
                                                      numEntries,
                                                      &counts);
            if (!matchPossible)
    /*
     * Add on index qual eval costs, much as in genericcostestimate
     */
-   cost_qual_eval(&index_qual_cost, indexQuals, root);
-   qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple;
-   cost_qual_eval(&index_qual_cost, indexOrderBys, root);
-   qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+   qual_arg_cost = other_operands_eval_cost(root, qinfos) +
+       orderby_operands_eval_cost(root, path);
    qual_op_cost = cpu_operator_cost *
        (list_length(indexQuals) + list_length(indexOrderBys));
-   qual_arg_cost -= qual_op_cost;
-   if (qual_arg_cost < 0)      /* just in case... */
-       qual_arg_cost = 0;
 
    *indexStartupCost += qual_arg_cost;
    *indexTotalCost += qual_arg_cost;
    List       *indexOrderBys = path->indexorderbys;
    double      numPages = index->pages;
    double      numTuples = index->tuples;
+   List       *qinfos;
    Cost        spc_seq_page_cost;
    Cost        spc_random_page_cost;
-   QualCost    index_qual_cost;
    double      qual_op_cost;
    double      qual_arg_cost;
 
+   /* Do preliminary analysis of indexquals */
+   qinfos = deconstruct_indexquals(path);
+
    /* fetch estimated page cost for tablespace containing index */
    get_tablespace_page_costs(index->reltablespace,
                              &spc_random_page_cost,
 
    /*
     * BRIN indexes are always read in full; use that as startup cost.
+    *
     * XXX maybe only include revmap pages here?
     */
    *indexStartupCost = spc_seq_page_cost * numPages * loop_count;
 
    /*
-    * To read a BRIN index there might be a bit of back and forth over regular
-    * pages, as revmap might point to them out of sequential order; calculate
-    * this as reading the whole index in random order.
+    * To read a BRIN index there might be a bit of back and forth over
+    * regular pages, as revmap might point to them out of sequential order;
+    * calculate this as reading the whole index in random order.
     */
    *indexTotalCost = spc_random_page_cost * numPages * loop_count;
 
    *indexSelectivity =
-       clauselist_selectivity(root, path->indexquals,
+       clauselist_selectivity(root, indexQuals,
                               path->indexinfo->rel->relid,
                               JOIN_INNER, NULL);
    *indexCorrelation = 1;
    /*
     * Add on index qual eval costs, much as in genericcostestimate.
     */
-   cost_qual_eval(&index_qual_cost, indexQuals, root);
-   qual_arg_cost = index_qual_cost.startup + index_qual_cost.per_tuple;
-   cost_qual_eval(&index_qual_cost, indexOrderBys, root);
-   qual_arg_cost += index_qual_cost.startup + index_qual_cost.per_tuple;
+   qual_arg_cost = other_operands_eval_cost(root, qinfos) +
+       orderby_operands_eval_cost(root, path);
    qual_op_cost = cpu_operator_cost *
        (list_length(indexQuals) + list_length(indexOrderBys));
-   qual_arg_cost -= qual_op_cost;
-   if (qual_arg_cost < 0)      /* just in case... */
-       qual_arg_cost = 0;
 
    *indexStartupCost += qual_arg_cost;
    *indexTotalCost += qual_arg_cost;