Oid sortop,
                          Datum *min, Datum *max);
 static RelOptInfo *find_join_input_rel(PlannerInfo *root, Relids relids);
-static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
 
 
 /*
  *-------------------------------------------------------------------------
  */
 
-/* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */
-static List *
-get_index_quals(List *indexclauses)
+/*
+ * Extract the actual indexquals (as RestrictInfos) from an IndexClause list
+ */
+List *
+get_quals_from_indexclauses(List *indexclauses)
 {
    List       *result = NIL;
    ListCell   *lc;
    return result;
 }
 
-List *
-deconstruct_indexquals(IndexPath *path)
-{
-   List       *result = NIL;
-   ListCell   *lc;
-
-   foreach(lc, path->indexclauses)
-   {
-       IndexClause *iclause = lfirst_node(IndexClause, lc);
-       int         indexcol = iclause->indexcol;
-       ListCell   *lc2;
-
-       foreach(lc2, iclause->indexquals)
-       {
-           RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
-           Expr       *clause = rinfo->clause;
-           IndexQualInfo *qinfo;
-
-           qinfo = (IndexQualInfo *) palloc(sizeof(IndexQualInfo));
-           qinfo->rinfo = rinfo;
-           qinfo->indexcol = indexcol;
-
-           if (IsA(clause, OpExpr))
-           {
-               qinfo->clause_op = ((OpExpr *) clause)->opno;
-               qinfo->other_operand = get_rightop(clause);
-           }
-           else if (IsA(clause, RowCompareExpr))
-           {
-               RowCompareExpr *rc = (RowCompareExpr *) clause;
-
-               qinfo->clause_op = linitial_oid(rc->opnos);
-               qinfo->other_operand = (Node *) rc->rargs;
-           }
-           else if (IsA(clause, ScalarArrayOpExpr))
-           {
-               ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
-
-               qinfo->clause_op = saop->opno;
-               qinfo->other_operand = (Node *) lsecond(saop->args);
-           }
-           else if (IsA(clause, NullTest))
-           {
-               qinfo->clause_op = InvalidOid;
-               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
+ * Compute the total evaluation cost of the comparison operands in a list
+ * of index qual expressions.  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.
+ * This can be used either on the result of get_quals_from_indexclauses(),
+ * or directly on an indexorderbys list.  In both cases, we expect that the
+ * index key expression is on the left side of binary clauses.
  */
-static Cost
-orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
+Cost
+index_other_operands_eval_cost(PlannerInfo *root, List *indexquals)
 {
    Cost        qual_arg_cost = 0;
    ListCell   *lc;
 
-   foreach(lc, path->indexorderbys)
+   foreach(lc, indexquals)
    {
        Expr       *clause = (Expr *) lfirst(lc);
        Node       *other_operand;
        QualCost    index_qual_cost;
 
+       /*
+        * Index quals will have RestrictInfos, indexorderbys won't.  Look
+        * through RestrictInfo if present.
+        */
+       if (IsA(clause, RestrictInfo))
+           clause = ((RestrictInfo *) clause)->clause;
+
        if (IsA(clause, OpExpr))
        {
-           other_operand = get_rightop(clause);
+           OpExpr     *op = (OpExpr *) clause;
+
+           other_operand = (Node *) lsecond(op->args);
+       }
+       else if (IsA(clause, RowCompareExpr))
+       {
+           RowCompareExpr *rc = (RowCompareExpr *) clause;
+
+           other_operand = (Node *) rc->rargs;
+       }
+       else if (IsA(clause, ScalarArrayOpExpr))
+       {
+           ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+
+           other_operand = (Node *) lsecond(saop->args);
+       }
+       else if (IsA(clause, NullTest))
+       {
+           other_operand = NULL;
        }
        else
        {
-           elog(ERROR, "unsupported indexorderby type: %d",
+           elog(ERROR, "unsupported indexqual type: %d",
                 (int) nodeTag(clause));
            other_operand = NULL;   /* keep compiler quiet */
        }
 genericcostestimate(PlannerInfo *root,
                    IndexPath *path,
                    double loop_count,
-                   List *qinfos,
                    GenericCosts *costs)
 {
    IndexOptInfo *index = path->indexinfo;
-   List       *indexQuals = get_index_quals(path->indexclauses);
+   List       *indexQuals = get_quals_from_indexclauses(path->indexclauses);
    List       *indexOrderBys = path->indexorderbys;
    Cost        indexStartupCost;
    Cost        indexTotalCost;
     * given indexquals to produce a more accurate idea of the index
     * selectivity.
     */
-   selectivityQuals = add_predicate_to_quals(index, indexQuals);
+   selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
 
    /*
     * Check for ScalarArrayOpExpr index quals, and estimate the number of
     * Detecting that that might be needed seems more expensive than it's
     * worth, though, considering all the other inaccuracies here ...
     */
-   qual_arg_cost = other_operands_eval_cost(root, qinfos) +
-       orderby_operands_eval_cost(root, path);
+   qual_arg_cost = index_other_operands_eval_cost(root, indexQuals) +
+       index_other_operands_eval_cost(root, indexOrderBys);
    qual_op_cost = cpu_operator_cost *
        (list_length(indexQuals) + list_length(indexOrderBys));
 
  * predicate_implied_by() and clauselist_selectivity(), but might be
  * problematic if the result were passed to other things.
  */
-static List *
-add_predicate_to_quals(IndexOptInfo *index, List *indexQuals)
+List *
+add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
 {
    List       *predExtraQuals = NIL;
    ListCell   *lc;
               double *indexPages)
 {
    IndexOptInfo *index = path->indexinfo;
-   List       *qinfos;
    GenericCosts costs;
    Oid         relid;
    AttrNumber  colnum;
    double      num_sa_scans;
    ListCell   *lc;
 
-   /* Do preliminary analysis of indexquals */
-   qinfos = deconstruct_indexquals(path);
-
    /*
     * For a btree scan, only leading '=' quals plus inequality quals for the
     * immediately next attribute contribute to index selectivity (these are
    found_saop = false;
    found_is_null_op = false;
    num_sa_scans = 1;
-   foreach(lc, qinfos)
+   foreach(lc, path->indexclauses)
    {
-       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(lc);
-       RestrictInfo *rinfo = qinfo->rinfo;
-       Expr       *clause = rinfo->clause;
-       Oid         clause_op;
-       int         op_strategy;
+       IndexClause *iclause = lfirst_node(IndexClause, lc);
+       ListCell   *lc2;
 
-       if (indexcol != qinfo->indexcol)
+       if (indexcol != iclause->indexcol)
        {
            /* Beginning of a new column's quals */
            if (!eqQualHere)
                break;          /* done if no '=' qual for indexcol */
            eqQualHere = false;
            indexcol++;
-           if (indexcol != qinfo->indexcol)
+           if (indexcol != iclause->indexcol)
                break;          /* no quals at all for indexcol */
        }
 
-       if (IsA(clause, ScalarArrayOpExpr))
+       /* Examine each indexqual associated with this index clause */
+       foreach(lc2, iclause->indexquals)
        {
-           int         alength = estimate_array_length(qinfo->other_operand);
+           RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+           Expr       *clause = rinfo->clause;
+           Oid         clause_op = InvalidOid;
+           int         op_strategy;
 
-           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;
+           if (IsA(clause, OpExpr))
+           {
+               OpExpr     *op = (OpExpr *) clause;
+
+               clause_op = op->opno;
+           }
+           else if (IsA(clause, RowCompareExpr))
+           {
+               RowCompareExpr *rc = (RowCompareExpr *) clause;
 
-           if (nt->nulltesttype == IS_NULL)
+               clause_op = linitial_oid(rc->opnos);
+           }
+           else if (IsA(clause, ScalarArrayOpExpr))
            {
-               found_is_null_op = true;
-               /* IS NULL is like = for selectivity determination purposes */
-               eqQualHere = true;
+               ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
+               Node       *other_operand = (Node *) lsecond(saop->args);
+               int         alength = estimate_array_length(other_operand);
+
+               clause_op = saop->opno;
+               found_saop = true;
+               /* count number of SA scans induced by indexBoundQuals only */
+               if (alength > 1)
+                   num_sa_scans *= alength;
            }
-       }
+           else if (IsA(clause, NullTest))
+           {
+               NullTest   *nt = (NullTest *) clause;
 
-       /* check for equality operator */
-       clause_op = qinfo->clause_op;
-       if (OidIsValid(clause_op))
-       {
-           op_strategy = get_op_opfamily_strategy(clause_op,
-                                                  index->opfamily[indexcol]);
-           Assert(op_strategy != 0);   /* not a member of opfamily?? */
-           if (op_strategy == BTEqualStrategyNumber)
-               eqQualHere = true;
-       }
+               if (nt->nulltesttype == IS_NULL)
+               {
+                   found_is_null_op = true;
+                   /* IS NULL is like = for selectivity purposes */
+                   eqQualHere = true;
+               }
+           }
+           else
+               elog(ERROR, "unsupported indexqual type: %d",
+                    (int) nodeTag(clause));
 
-       indexBoundQuals = lappend(indexBoundQuals, rinfo);
+           /* check for equality operator */
+           if (OidIsValid(clause_op))
+           {
+               op_strategy = get_op_opfamily_strategy(clause_op,
+                                                      index->opfamily[indexcol]);
+               Assert(op_strategy != 0);   /* not a member of opfamily?? */
+               if (op_strategy == BTEqualStrategyNumber)
+                   eqQualHere = true;
+           }
+
+           indexBoundQuals = lappend(indexBoundQuals, rinfo);
+       }
    }
 
    /*
         * index-bound quals to produce a more accurate idea of the number of
         * rows covered by the bound conditions.
         */
-       selectivityQuals = add_predicate_to_quals(index, indexBoundQuals);
+       selectivityQuals = add_predicate_to_index_quals(index, indexBoundQuals);
 
        btreeSelectivity = clauselist_selectivity(root, selectivityQuals,
                                                  index->rel->relid,
    MemSet(&costs, 0, sizeof(costs));
    costs.numIndexTuples = numIndexTuples;
 
-   genericcostestimate(root, path, loop_count, qinfos, &costs);
+   genericcostestimate(root, path, loop_count, &costs);
 
    /*
     * Add a CPU-cost component to represent the costs of initial btree
                 Selectivity *indexSelectivity, double *indexCorrelation,
                 double *indexPages)
 {
-   List       *qinfos;
    GenericCosts costs;
 
-   /* Do preliminary analysis of indexquals */
-   qinfos = deconstruct_indexquals(path);
-
    MemSet(&costs, 0, sizeof(costs));
 
-   genericcostestimate(root, path, loop_count, qinfos, &costs);
+   genericcostestimate(root, path, loop_count, &costs);
 
    /*
     * A hash index has no descent costs as such, since the index AM can go
                 double *indexPages)
 {
    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, qinfos, &costs);
+   genericcostestimate(root, path, loop_count, &costs);
 
    /*
     * We model index descent costs similarly to those for btree, but to do
                double *indexPages)
 {
    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, qinfos, &costs);
+   genericcostestimate(root, path, loop_count, &costs);
 
    /*
     * We model index descent costs similarly to those for btree, but to do
 static bool
 gincost_opexpr(PlannerInfo *root,
               IndexOptInfo *index,
-              IndexQualInfo *qinfo,
+              int indexcol,
+              OpExpr *clause,
               GinQualCounts *counts)
 {
-   int         indexcol = qinfo->indexcol;
-   Oid         clause_op = qinfo->clause_op;
-   Node       *operand = qinfo->other_operand;
+   Oid         clause_op = clause->opno;
+   Node       *operand = (Node *) lsecond(clause->args);
 
    /* aggressively reduce to a constant, and look through relabeling */
    operand = estimate_expression_value(root, operand);
 static bool
 gincost_scalararrayopexpr(PlannerInfo *root,
                          IndexOptInfo *index,
-                         IndexQualInfo *qinfo,
+                         int indexcol,
+                         ScalarArrayOpExpr *clause,
                          double numIndexEntries,
                          GinQualCounts *counts)
 {
-   int         indexcol = qinfo->indexcol;
-   Oid         clause_op = qinfo->clause_op;
-   Node       *rightop = qinfo->other_operand;
+   Oid         clause_op = clause->opno;
+   Node       *rightop = (Node *) lsecond(clause->args);
    ArrayType  *arrayval;
    int16       elmlen;
    bool        elmbyval;
    int         numPossible = 0;
    int         i;
 
-   Assert(((ScalarArrayOpExpr *) qinfo->rinfo->clause)->useOr);
+   Assert(clause->useOr);
 
    /* aggressively reduce to a constant, and look through relabeling */
    rightop = estimate_expression_value(root, rightop);
                double *indexPages)
 {
    IndexOptInfo *index = path->indexinfo;
-   List       *indexQuals = get_index_quals(path->indexclauses);
-   List       *indexOrderBys = path->indexorderbys;
-   List       *qinfos;
-   ListCell   *l;
+   List       *indexQuals = get_quals_from_indexclauses(path->indexclauses);
    List       *selectivityQuals;
    double      numPages = index->pages,
                numTuples = index->tuples;
                outer_scans;
    Relation    indexRel;
    GinStatsData ginStats;
-
-   /* Do preliminary analysis of indexquals */
-   qinfos = deconstruct_indexquals(path);
+   ListCell   *lc;
 
    /*
     * Obtain statistical information from the meta page, if possible.  Else
     * quals to produce a more accurate idea of the number of rows covered by
     * the bound conditions.
     */
-   selectivityQuals = add_predicate_to_quals(index, indexQuals);
+   selectivityQuals = add_predicate_to_index_quals(index, indexQuals);
 
    /* Estimate the fraction of main-table tuples that will be visited */
    *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
    counts.arrayScans = 1;
    matchPossible = true;
 
-   foreach(l, qinfos)
+   foreach(lc, path->indexclauses)
    {
-       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
-       Expr       *clause = qinfo->rinfo->clause;
+       IndexClause *iclause = lfirst_node(IndexClause, lc);
+       ListCell   *lc2;
 
-       if (IsA(clause, OpExpr))
-       {
-           matchPossible = gincost_opexpr(root,
-                                          index,
-                                          qinfo,
-                                          &counts);
-           if (!matchPossible)
-               break;
-       }
-       else if (IsA(clause, ScalarArrayOpExpr))
-       {
-           matchPossible = gincost_scalararrayopexpr(root,
-                                                     index,
-                                                     qinfo,
-                                                     numEntries,
-                                                     &counts);
-           if (!matchPossible)
-               break;
-       }
-       else
+       foreach(lc2, iclause->indexquals)
        {
-           /* shouldn't be anything else for a GIN index */
-           elog(ERROR, "unsupported GIN indexqual type: %d",
-                (int) nodeTag(clause));
+           RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
+           Expr       *clause = rinfo->clause;
+
+           if (IsA(clause, OpExpr))
+           {
+               matchPossible = gincost_opexpr(root,
+                                              index,
+                                              iclause->indexcol,
+                                              (OpExpr *) clause,
+                                              &counts);
+               if (!matchPossible)
+                   break;
+           }
+           else if (IsA(clause, ScalarArrayOpExpr))
+           {
+               matchPossible = gincost_scalararrayopexpr(root,
+                                                         index,
+                                                         iclause->indexcol,
+                                                         (ScalarArrayOpExpr *) clause,
+                                                         numEntries,
+                                                         &counts);
+               if (!matchPossible)
+                   break;
+           }
+           else
+           {
+               /* shouldn't be anything else for a GIN index */
+               elog(ERROR, "unsupported GIN indexqual type: %d",
+                    (int) nodeTag(clause));
+           }
        }
    }
 
        dataPagesFetched * spc_random_page_cost;
 
    /*
-    * Add on index qual eval costs, much as in genericcostestimate
+    * Add on index qual eval costs, much as in genericcostestimate.  But we
+    * can disregard indexorderbys, since GIN doesn't support those.
     */
-   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 = index_other_operands_eval_cost(root, indexQuals);
+   qual_op_cost = cpu_operator_cost * list_length(indexQuals);
 
    *indexStartupCost += qual_arg_cost;
    *indexTotalCost += qual_arg_cost;
                 double *indexPages)
 {
    IndexOptInfo *index = path->indexinfo;
-   List       *indexQuals = get_index_quals(path->indexclauses);
+   List       *indexQuals = get_quals_from_indexclauses(path->indexclauses);
    double      numPages = index->pages;
    RelOptInfo *baserel = index->rel;
    RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
-   List       *qinfos;
    Cost        spc_seq_page_cost;
    Cost        spc_random_page_cost;
    double      qual_arg_cost;
     */
    *indexCorrelation = 0;
 
-   qinfos = deconstruct_indexquals(path);
-   foreach(l, qinfos)
+   foreach(l, path->indexclauses)
    {
-       IndexQualInfo *qinfo = (IndexQualInfo *) lfirst(l);
-       AttrNumber  attnum = index->indexkeys[qinfo->indexcol];
+       IndexClause *iclause = lfirst_node(IndexClause, l);
+       AttrNumber  attnum = index->indexkeys[iclause->indexcol];
 
        /* attempt to lookup stats in relation for this index column */
        if (attnum != 0)
             */
 
            /* get the attnum from the 0-based index. */
-           attnum = qinfo->indexcol + 1;
+           attnum = iclause->indexcol + 1;
 
            if (get_index_stats_hook &&
                (*get_index_stats_hook) (root, index->indexoid, attnum, &vardata))
 
    /*
     * Compute the index qual costs, much as in genericcostestimate, to add to
-    * the index costs.
+    * the index costs.  We can disregard indexorderbys, since BRIN doesn't
+    * support those.
     */
-   qual_arg_cost = other_operands_eval_cost(root, qinfos) +
-       orderby_operands_eval_cost(root, path);
+   qual_arg_cost = index_other_operands_eval_cost(root, indexQuals);
 
    /*
     * Compute the startup cost as the cost to read the whole revmap