diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b486ef3..3403269 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3460,4 +3460,18 @@ supportfn(internal) returns internal
     This can be done by a support function that implements
     the <literal>SupportRequestRows</literal> request type.
    </para>
+
+   <para>
+    For target functions that return boolean, it may be possible to
+    convert a function call appearing in WHERE into an indexable operator
+    clause or clauses.  The converted clauses might be exactly equivalent
+    to the function's condition, or they could be somewhat weaker (that is,
+    they might accept some values that the function condition does not).
+    In the latter case the index condition is said to
+    be <firstterm>lossy</firstterm>; it can still be used to scan an index,
+    but the function call will have to be executed for each row returned by
+    the index to see if it really passes the WHERE condition or not.
+    To create such conditions, the support function must implement
+    the <literal>SupportRequestIndexCondition</literal> request type.
+   </para>
   </sect1>
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 51d2da5..fdad50e 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -20,23 +20,19 @@
 #include "access/stratnum.h"
 #include "access/sysattr.h"
 #include "catalog/pg_am.h"
-#include "catalog/pg_collation.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
-#include "optimizer/clauses.h"
+#include "nodes/supportnodes.h"
 #include "optimizer/cost.h"
 #include "optimizer/optimizer.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
-#include "utils/builtins.h"
-#include "utils/bytea.h"
 #include "utils/lsyscache.h"
-#include "utils/pg_locale.h"
 #include "utils/selfuncs.h"
 
 
@@ -136,7 +132,7 @@ static double adjust_rowcount_for_semijoins(PlannerInfo *root,
 							  Index outer_relid,
 							  double rowcount);
 static double approximate_joinrel_size(PlannerInfo *root, Relids relids);
-static void match_restriction_clauses_to_index(RelOptInfo *rel,
+static void match_restriction_clauses_to_index(PlannerInfo *root,
 								   IndexOptInfo *index,
 								   IndexClauseSet *clauseset);
 static void match_join_clauses_to_index(PlannerInfo *root,
@@ -146,22 +142,45 @@ static void match_join_clauses_to_index(PlannerInfo *root,
 static void match_eclass_clauses_to_index(PlannerInfo *root,
 							  IndexOptInfo *index,
 							  IndexClauseSet *clauseset);
-static void match_clauses_to_index(IndexOptInfo *index,
+static void match_clauses_to_index(PlannerInfo *root,
 					   List *clauses,
+					   IndexOptInfo *index,
 					   IndexClauseSet *clauseset);
-static void match_clause_to_index(IndexOptInfo *index,
+static void match_clause_to_index(PlannerInfo *root,
 					  RestrictInfo *rinfo,
+					  IndexOptInfo *index,
 					  IndexClauseSet *clauseset);
-static bool match_clause_to_indexcol(IndexOptInfo *index,
+static IndexClause *match_clause_to_indexcol(PlannerInfo *root,
+						 RestrictInfo *rinfo,
 						 int indexcol,
-						 RestrictInfo *rinfo);
-static bool is_indexable_operator(Oid expr_op, Oid opfamily,
-					  bool indexkey_on_left);
-static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
+						 IndexOptInfo *index);
+static IndexClause *match_boolean_index_clause(RestrictInfo *rinfo,
+						   int indexcol, IndexOptInfo *index);
+static IndexClause *match_opclause_to_indexcol(PlannerInfo *root,
+						   RestrictInfo *rinfo,
+						   int indexcol,
+						   IndexOptInfo *index);
+static IndexClause *match_funcclause_to_indexcol(PlannerInfo *root,
+							 RestrictInfo *rinfo,
+							 int indexcol,
+							 IndexOptInfo *index);
+static IndexClause *get_index_clause_from_support(PlannerInfo *root,
+							  RestrictInfo *rinfo,
+							  Oid funcid,
+							  int indexarg,
+							  int indexcol,
+							  IndexOptInfo *index);
+static IndexClause *match_saopclause_to_indexcol(RestrictInfo *rinfo,
+							 int indexcol,
+							 IndexOptInfo *index);
+static IndexClause *match_rowcompare_to_indexcol(RestrictInfo *rinfo,
 							 int indexcol,
-							 Oid opfamily,
-							 Oid idxcollation,
-							 RowCompareExpr *clause);
+							 IndexOptInfo *index);
+static IndexClause *expand_indexqual_rowcompare(RestrictInfo *rinfo,
+							int indexcol,
+							IndexOptInfo *index,
+							Oid expr_op,
+							bool var_on_left);
 static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p);
@@ -170,30 +189,6 @@ static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
 static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
 						   EquivalenceClass *ec, EquivalenceMember *em,
 						   void *arg);
-static bool match_boolean_index_clause(Node *clause, int indexcol,
-						   IndexOptInfo *index);
-static bool match_special_index_operator(Expr *clause,
-							 Oid opfamily, Oid idxcollation,
-							 bool indexkey_on_left);
-static IndexClause *expand_indexqual_conditions(IndexOptInfo *index,
-							int indexcol,
-							RestrictInfo *rinfo);
-static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
-							IndexOptInfo *index);
-static List *expand_indexqual_opclause(RestrictInfo *rinfo,
-						  Oid opfamily, Oid idxcollation,
-						  bool *lossy);
-static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
-							IndexOptInfo *index,
-							int indexcol,
-							List **indexcolnos,
-							bool *lossy);
-static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
-			 Const *prefix, Pattern_Prefix_Status pstatus);
-static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
-					 Datum rightop);
-static Datum string_to_datum(const char *str, Oid datatype);
-static Const *string_to_const(const char *str, Oid datatype);
 
 
 /*
@@ -272,7 +267,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
 		 * Identify the restriction clauses that can match the index.
 		 */
 		MemSet(&rclauseset, 0, sizeof(rclauseset));
-		match_restriction_clauses_to_index(rel, index, &rclauseset);
+		match_restriction_clauses_to_index(root, index, &rclauseset);
 
 		/*
 		 * Build index paths from the restriction clauses.  These will be
@@ -1224,7 +1219,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
 		 * Identify the restriction clauses that can match the index.
 		 */
 		MemSet(&clauseset, 0, sizeof(clauseset));
-		match_clauses_to_index(index, clauses, &clauseset);
+		match_clauses_to_index(root, clauses, index, &clauseset);
 
 		/*
 		 * If no matches so far, and the index predicate isn't useful, we
@@ -1236,7 +1231,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
 		/*
 		 * Add "other" restriction clauses to the clauseset.
 		 */
-		match_clauses_to_index(index, other_clauses, &clauseset);
+		match_clauses_to_index(root, other_clauses, index, &clauseset);
 
 		/*
 		 * Construct paths if possible.
@@ -2148,11 +2143,12 @@ approximate_joinrel_size(PlannerInfo *root, Relids relids)
  *	  Matching clauses are added to *clauseset.
  */
 static void
-match_restriction_clauses_to_index(RelOptInfo *rel, IndexOptInfo *index,
+match_restriction_clauses_to_index(PlannerInfo *root,
+								   IndexOptInfo *index,
 								   IndexClauseSet *clauseset)
 {
 	/* We can ignore clauses that are implied by the index predicate */
-	match_clauses_to_index(index, index->indrestrictinfo, clauseset);
+	match_clauses_to_index(root, index->indrestrictinfo, index, clauseset);
 }
 
 /*
@@ -2182,7 +2178,7 @@ match_join_clauses_to_index(PlannerInfo *root,
 		if (restriction_is_or_clause(rinfo))
 			*joinorclauses = lappend(*joinorclauses, rinfo);
 		else
-			match_clause_to_index(index, rinfo, clauseset);
+			match_clause_to_index(root, rinfo, index, clauseset);
 	}
 }
 
@@ -2220,7 +2216,7 @@ match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,
 		 * since for non-btree indexes the EC's equality operators might not
 		 * be in the index opclass (cf ec_member_matches_indexcol).
 		 */
-		match_clauses_to_index(index, clauses, clauseset);
+		match_clauses_to_index(root, clauses, index, clauseset);
 	}
 }
 
@@ -2230,8 +2226,9 @@ match_eclass_clauses_to_index(PlannerInfo *root, IndexOptInfo *index,
  *	  Matching clauses are added to *clauseset.
  */
 static void
-match_clauses_to_index(IndexOptInfo *index,
+match_clauses_to_index(PlannerInfo *root,
 					   List *clauses,
+					   IndexOptInfo *index,
 					   IndexClauseSet *clauseset)
 {
 	ListCell   *lc;
@@ -2240,7 +2237,7 @@ match_clauses_to_index(IndexOptInfo *index,
 	{
 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
 
-		match_clause_to_index(index, rinfo, clauseset);
+		match_clause_to_index(root, rinfo, index, clauseset);
 	}
 }
 
@@ -2262,8 +2259,9 @@ match_clauses_to_index(IndexOptInfo *index,
  * same clause multiple times with different index columns.
  */
 static void
-match_clause_to_index(IndexOptInfo *index,
+match_clause_to_index(PlannerInfo *root,
 					  RestrictInfo *rinfo,
+					  IndexOptInfo *index,
 					  IndexClauseSet *clauseset)
 {
 	int			indexcol;
@@ -2287,6 +2285,7 @@ match_clause_to_index(IndexOptInfo *index,
 	/* OK, check each index key column for a match */
 	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
 	{
+		IndexClause *iclause;
 		ListCell   *lc;
 
 		/* Ignore duplicates */
@@ -2298,17 +2297,14 @@ match_clause_to_index(IndexOptInfo *index,
 				return;
 		}
 
-		/*
-		 * XXX this should be changed so that we generate an IndexClause
-		 * immediately upon matching, to avoid repeated work.  To-do soon.
-		 */
-		if (match_clause_to_indexcol(index,
-									 indexcol,
-									 rinfo))
+		/* OK, try to match the clause to the index column */
+		iclause = match_clause_to_indexcol(root,
+										   rinfo,
+										   indexcol,
+										   index);
+		if (iclause)
 		{
-			IndexClause *iclause;
-
-			iclause = expand_indexqual_conditions(index, indexcol, rinfo);
+			/* Success, so record it */
 			clauseset->indexclauses[indexcol] =
 				lappend(clauseset->indexclauses[indexcol], iclause);
 			clauseset->nonempty = true;
@@ -2319,16 +2315,15 @@ match_clause_to_index(IndexOptInfo *index,
 
 /*
  * match_clause_to_indexcol()
- *	  Determines whether a restriction clause matches a column of an index.
+ *	  Determine whether a restriction clause matches a column of an index,
+ *	  and if so, build an IndexClause node describing the details.
  *
- *	  To match an index normally, the clause:
+ *	  To match an index normally, an operator clause:
  *
  *	  (1)  must be in the form (indexkey op const) or (const op indexkey);
  *		   and
- *	  (2)  must contain an operator which is in the same family as the index
- *		   operator for this column, or is a "special" operator as recognized
- *		   by match_special_index_operator();
- *		   and
+ *	  (2)  must contain an operator which is in the index's operator family
+ *		   for this column; and
  *	  (3)  must match the collation of the index, if collation is relevant.
  *
  *	  Our definition of "const" is exceedingly liberal: we allow anything that
@@ -2346,8 +2341,8 @@ match_clause_to_index(IndexOptInfo *index,
  *	  Presently, the executor can only deal with indexquals that have the
  *	  indexkey on the left, so we can only use clauses that have the indexkey
  *	  on the right if we can commute the clause to put the key on the left.
- *	  We do not actually do the commuting here, but we check whether a
- *	  suitable commutator operator is available.
+ *	  We handle that by generating an IndexClause with the correctly-commuted
+ *	  opclause as a derived indexqual.
  *
  *	  If the index has a collation, the clause must have the same collation.
  *	  For collation-less indexes, we assume it doesn't matter; this is
@@ -2357,12 +2352,7 @@ match_clause_to_index(IndexOptInfo *index,
  *	  embodied in the macro IndexCollMatchesExprColl.)
  *
  *	  It is also possible to match RowCompareExpr clauses to indexes (but
- *	  currently, only btree indexes handle this).  In this routine we will
- *	  report a match if the first column of the row comparison matches the
- *	  target index column.  This is sufficient to guarantee that some index
- *	  condition can be constructed from the RowCompareExpr --- whether the
- *	  remaining columns match the index too is considered in
- *	  expand_indexqual_rowcompare().
+ *	  currently, only btree indexes handle this).
  *
  *	  It is also possible to match ScalarArrayOpExpr clauses to indexes, when
  *	  the clause is of the form "indexkey op ANY (arrayconst)".
@@ -2370,82 +2360,71 @@ match_clause_to_index(IndexOptInfo *index,
  *	  For boolean indexes, it is also possible to match the clause directly
  *	  to the indexkey; or perhaps the clause is (NOT indexkey).
  *
- * 'index' is the index of interest.
- * 'indexcol' is a column number of 'index' (counting from 0).
+ *	  And, last but not least, some operators and functions can be processed
+ *	  to derive (typically lossy) indexquals from a clause that isn't in
+ *	  itself indexable.  If we see that any operand of an OpExpr or FuncExpr
+ *	  matches the index key, and the function has a planner support function
+ *	  attached to it, we'll invoke the support function to see if such an
+ *	  indexqual can be built.
+ *
  * 'rinfo' is the clause to be tested (as a RestrictInfo node).
+ * 'indexcol' is a column number of 'index' (counting from 0).
+ * 'index' is the index of interest.
  *
- * Returns true if the clause can be used with this index key.
+ * Returns an IndexClause if the clause can be used with this index key,
+ * or NULL if not.
  *
- * NOTE:  returns false if clause is an OR or AND clause; it is the
+ * NOTE:  returns NULL if clause is an OR or AND clause; it is the
  * responsibility of higher-level routines to cope with those.
  */
-static bool
-match_clause_to_indexcol(IndexOptInfo *index,
+static IndexClause *
+match_clause_to_indexcol(PlannerInfo *root,
+						 RestrictInfo *rinfo,
 						 int indexcol,
-						 RestrictInfo *rinfo)
+						 IndexOptInfo *index)
 {
+	IndexClause *iclause;
 	Expr	   *clause = rinfo->clause;
-	Index		index_relid = index->rel->relid;
 	Oid			opfamily;
-	Oid			idxcollation;
-	Node	   *leftop,
-			   *rightop;
-	Relids		left_relids;
-	Relids		right_relids;
-	Oid			expr_op;
-	Oid			expr_coll;
-	bool		plain_op;
 
 	Assert(indexcol < index->nkeycolumns);
 
-	opfamily = index->opfamily[indexcol];
-	idxcollation = index->indexcollations[indexcol];
+	/*
+	 * Historically this code has coped with NULL clauses.  That's probably
+	 * not possible anymore, but we might as well continue to cope.
+	 */
+	if (clause == NULL)
+		return NULL;
 
 	/* First check for boolean-index cases. */
+	opfamily = index->opfamily[indexcol];
 	if (IsBooleanOpfamily(opfamily))
 	{
-		if (match_boolean_index_clause((Node *) clause, indexcol, index))
-			return true;
+		iclause = match_boolean_index_clause(rinfo, indexcol, index);
+		if (iclause)
+			return iclause;
 	}
 
 	/*
-	 * Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
-	 * (which is always binary, by definition).  Or it could be a
-	 * RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().
-	 * Or, if the index supports it, we can handle IS NULL/NOT NULL clauses.
+	 * Clause must be an opclause, funcclause, ScalarArrayOpExpr, or
+	 * RowCompareExpr.  Or, if the index supports it, we can handle IS
+	 * NULL/NOT NULL clauses.
 	 */
-	if (is_opclause(clause))
+	if (IsA(clause, OpExpr))
+	{
+		return match_opclause_to_indexcol(root, rinfo, indexcol, index);
+	}
+	else if (IsA(clause, FuncExpr))
 	{
-		leftop = get_leftop(clause);
-		rightop = get_rightop(clause);
-		if (!leftop || !rightop)
-			return false;
-		left_relids = rinfo->left_relids;
-		right_relids = rinfo->right_relids;
-		expr_op = ((OpExpr *) clause)->opno;
-		expr_coll = ((OpExpr *) clause)->inputcollid;
-		plain_op = true;
+		return match_funcclause_to_indexcol(root, rinfo, indexcol, index);
 	}
-	else if (clause && IsA(clause, ScalarArrayOpExpr))
+	else if (IsA(clause, ScalarArrayOpExpr))
 	{
-		ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
-
-		/* We only accept ANY clauses, not ALL */
-		if (!saop->useOr)
-			return false;
-		leftop = (Node *) linitial(saop->args);
-		rightop = (Node *) lsecond(saop->args);
-		left_relids = NULL;		/* not actually needed */
-		right_relids = pull_varnos(rightop);
-		expr_op = saop->opno;
-		expr_coll = saop->inputcollid;
-		plain_op = false;
+		return match_saopclause_to_indexcol(rinfo, indexcol, index);
 	}
-	else if (clause && IsA(clause, RowCompareExpr))
+	else if (IsA(clause, RowCompareExpr))
 	{
-		return match_rowcompare_to_indexcol(index, indexcol,
-											opfamily, idxcollation,
-											(RowCompareExpr *) clause);
+		return match_rowcompare_to_indexcol(rinfo, indexcol, index);
 	}
 	else if (index->amsearchnulls && IsA(clause, NullTest))
 	{
@@ -2453,101 +2432,441 @@ match_clause_to_indexcol(IndexOptInfo *index,
 
 		if (!nt->argisrow &&
 			match_index_to_operand((Node *) nt->arg, indexcol, index))
-			return true;
-		return false;
+		{
+			iclause = makeNode(IndexClause);
+			iclause->rinfo = rinfo;
+			iclause->indexquals = NIL;
+			iclause->lossy = false;
+			iclause->indexcol = indexcol;
+			iclause->indexcols = NIL;
+			return iclause;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * match_boolean_index_clause
+ *	  Recognize restriction clauses that can be matched to a boolean index.
+ *
+ * The idea here is that, for an index on a boolean column that supports the
+ * BooleanEqualOperator, we can transform a plain reference to the indexkey
+ * into "indexkey = true", or "NOT indexkey" into "indexkey = false", etc,
+ * so as to make the expression indexable using the index's "=" operator.
+ * Since Postgres 8.1, we must do this because constant simplification does
+ * the reverse transformation; without this code there'd be no way to use
+ * such an index at all.
+ *
+ * This should be called only when IsBooleanOpfamily() recognizes the
+ * index's operator family.  We check to see if the clause matches the
+ * index's key, and if so, build a suitable IndexClause.
+ */
+static IndexClause *
+match_boolean_index_clause(RestrictInfo *rinfo,
+						   int indexcol,
+						   IndexOptInfo *index)
+{
+	Node	   *clause = (Node *) rinfo->clause;
+	Expr	   *op = NULL;
+
+	/* Direct match? */
+	if (match_index_to_operand(clause, indexcol, index))
+	{
+		/* convert to indexkey = TRUE */
+		op = make_opclause(BooleanEqualOperator, BOOLOID, false,
+						   (Expr *) clause,
+						   (Expr *) makeBoolConst(true, false),
+						   InvalidOid, InvalidOid);
+	}
+	/* NOT clause? */
+	else if (is_notclause(clause))
+	{
+		Node	   *arg = (Node *) get_notclausearg((Expr *) clause);
+
+		if (match_index_to_operand(arg, indexcol, index))
+		{
+			/* convert to indexkey = FALSE */
+			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
+							   (Expr *) arg,
+							   (Expr *) makeBoolConst(false, false),
+							   InvalidOid, InvalidOid);
+		}
+	}
+
+	/*
+	 * Since we only consider clauses at top level of WHERE, we can convert
+	 * indexkey IS TRUE and indexkey IS FALSE to index searches as well.  The
+	 * different meaning for NULL isn't important.
+	 */
+	else if (clause && IsA(clause, BooleanTest))
+	{
+		BooleanTest *btest = (BooleanTest *) clause;
+		Node	   *arg = (Node *) btest->arg;
+
+		if (btest->booltesttype == IS_TRUE &&
+			match_index_to_operand(arg, indexcol, index))
+		{
+			/* convert to indexkey = TRUE */
+			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
+							   (Expr *) arg,
+							   (Expr *) makeBoolConst(true, false),
+							   InvalidOid, InvalidOid);
+		}
+		else if (btest->booltesttype == IS_FALSE &&
+				 match_index_to_operand(arg, indexcol, index))
+		{
+			/* convert to indexkey = FALSE */
+			op = make_opclause(BooleanEqualOperator, BOOLOID, false,
+							   (Expr *) arg,
+							   (Expr *) makeBoolConst(false, false),
+							   InvalidOid, InvalidOid);
+		}
+	}
+
+	/*
+	 * If we successfully made an operator clause from the given qual, we must
+	 * wrap it in an IndexClause.  It's not lossy.
+	 */
+	if (op)
+	{
+		IndexClause *iclause = makeNode(IndexClause);
+
+		iclause->rinfo = rinfo;
+		iclause->indexquals = list_make1(make_simple_restrictinfo(op));
+		iclause->lossy = false;
+		iclause->indexcol = indexcol;
+		iclause->indexcols = NIL;
+		return iclause;
 	}
-	else
-		return false;
+
+	return NULL;
+}
+
+/*
+ * match_opclause_to_indexcol()
+ *	  Handles the OpExpr case for match_clause_to_indexcol(),
+ *	  which see for comments.
+ */
+static IndexClause *
+match_opclause_to_indexcol(PlannerInfo *root,
+						   RestrictInfo *rinfo,
+						   int indexcol,
+						   IndexOptInfo *index)
+{
+	IndexClause *iclause;
+	OpExpr	   *clause = (OpExpr *) rinfo->clause;
+	Node	   *leftop,
+			   *rightop;
+	Oid			expr_op;
+	Oid			expr_coll;
+	Index		index_relid;
+	Oid			opfamily;
+	Oid			idxcollation;
+
+	/*
+	 * Only binary operators need apply.  (In theory, a planner support
+	 * function could do something with a unary operator, but it seems
+	 * unlikely to be worth the cycles to check.)
+	 */
+	if (list_length(clause->args) != 2)
+		return NULL;
+
+	leftop = (Node *) linitial(clause->args);
+	rightop = (Node *) lsecond(clause->args);
+	expr_op = clause->opno;
+	expr_coll = clause->inputcollid;
+
+	index_relid = index->rel->relid;
+	opfamily = index->opfamily[indexcol];
+	idxcollation = index->indexcollations[indexcol];
 
 	/*
 	 * Check for clauses of the form: (indexkey operator constant) or
-	 * (constant operator indexkey).  See above notes about const-ness.
+	 * (constant operator indexkey).  See match_clause_to_indexcol's notes
+	 * about const-ness.
+	 *
+	 * Note that we don't ask the support function about clauses that don't
+	 * have one of these forms.  Again, in principle it might be possible to
+	 * do something, but it seems unlikely to be worth the cycles to check.
 	 */
 	if (match_index_to_operand(leftop, indexcol, index) &&
-		!bms_is_member(index_relid, right_relids) &&
+		!bms_is_member(index_relid, rinfo->right_relids) &&
 		!contain_volatile_functions(rightop))
 	{
 		if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&
-			is_indexable_operator(expr_op, opfamily, true))
-			return true;
+			op_in_opfamily(expr_op, opfamily))
+		{
+			iclause = makeNode(IndexClause);
+			iclause->rinfo = rinfo;
+			iclause->indexquals = NIL;
+			iclause->lossy = false;
+			iclause->indexcol = indexcol;
+			iclause->indexcols = NIL;
+			return iclause;
+		}
 
 		/*
-		 * If we didn't find a member of the index's opfamily, see whether it
-		 * is a "special" indexable operator.
+		 * If we didn't find a member of the index's opfamily, try the support
+		 * function for the operator's underlying function.
 		 */
-		if (plain_op &&
-			match_special_index_operator(clause, opfamily,
-										 idxcollation, true))
-			return true;
-		return false;
+		set_opfuncid(clause);	/* make sure we have opfuncid */
+		return get_index_clause_from_support(root,
+											 rinfo,
+											 clause->opfuncid,
+											 0, /* indexarg on left */
+											 indexcol,
+											 index);
 	}
 
-	if (plain_op &&
-		match_index_to_operand(rightop, indexcol, index) &&
-		!bms_is_member(index_relid, left_relids) &&
+	if (match_index_to_operand(rightop, indexcol, index) &&
+		!bms_is_member(index_relid, rinfo->left_relids) &&
 		!contain_volatile_functions(leftop))
 	{
-		if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&
-			is_indexable_operator(expr_op, opfamily, false))
-			return true;
+		if (IndexCollMatchesExprColl(idxcollation, expr_coll))
+		{
+			Oid			comm_op = get_commutator(expr_op);
+
+			if (OidIsValid(comm_op) &&
+				op_in_opfamily(comm_op, opfamily))
+			{
+				RestrictInfo *commrinfo;
+
+				/* Build a commuted OpExpr and RestrictInfo */
+				commrinfo = commute_restrictinfo(rinfo, comm_op);
+
+				/* Make an IndexClause showing that as a derived qual */
+				iclause = makeNode(IndexClause);
+				iclause->rinfo = rinfo;
+				iclause->indexquals = list_make1(commrinfo);
+				iclause->lossy = false;
+				iclause->indexcol = indexcol;
+				iclause->indexcols = NIL;
+				return iclause;
+			}
+		}
 
 		/*
-		 * If we didn't find a member of the index's opfamily, see whether it
-		 * is a "special" indexable operator.
+		 * If we didn't find a member of the index's opfamily, try the support
+		 * function for the operator's underlying function.
 		 */
-		if (match_special_index_operator(clause, opfamily,
-										 idxcollation, false))
-			return true;
-		return false;
+		set_opfuncid(clause);	/* make sure we have opfuncid */
+		return get_index_clause_from_support(root,
+											 rinfo,
+											 clause->opfuncid,
+											 1, /* indexarg on right */
+											 indexcol,
+											 index);
 	}
 
-	return false;
+	return NULL;
 }
 
 /*
- * is_indexable_operator
- *	  Does the operator match the specified index opfamily?
- *
- * If the indexkey is on the right, what we actually want to know
- * is whether the operator has a commutator operator that matches
- * the opfamily.
+ * match_funcclause_to_indexcol()
+ *	  Handles the FuncExpr case for match_clause_to_indexcol(),
+ *	  which see for comments.
  */
-static bool
-is_indexable_operator(Oid expr_op, Oid opfamily, bool indexkey_on_left)
+static IndexClause *
+match_funcclause_to_indexcol(PlannerInfo *root,
+							 RestrictInfo *rinfo,
+							 int indexcol,
+							 IndexOptInfo *index)
 {
-	/* Get the commuted operator if necessary */
-	if (!indexkey_on_left)
+	FuncExpr   *clause = (FuncExpr *) rinfo->clause;
+	int			indexarg;
+	ListCell   *lc;
+
+	/*
+	 * We have no built-in intelligence about function clauses, but if there's
+	 * a planner support function, it might be able to do something.  But, to
+	 * cut down on wasted planning cycles, only call the support function if
+	 * at least one argument matches the target index column.
+	 *
+	 * Note that we don't insist on the other arguments being pseudoconstants;
+	 * the support function has to check that.  This is to allow cases where
+	 * only some of the other arguments need to be included in the indexqual.
+	 */
+	indexarg = 0;
+	foreach(lc, clause->args)
 	{
-		expr_op = get_commutator(expr_op);
-		if (expr_op == InvalidOid)
-			return false;
+		Node	   *op = (Node *) lfirst(lc);
+
+		if (match_index_to_operand(op, indexcol, index))
+		{
+			return get_index_clause_from_support(root,
+												 rinfo,
+												 clause->funcid,
+												 indexarg,
+												 indexcol,
+												 index);
+		}
+
+		indexarg++;
+	}
+
+	return NULL;
+}
+
+/*
+ * get_index_clause_from_support()
+ *		If the function has a planner support function, try to construct
+ *		an IndexClause using indexquals created by the support function.
+ */
+static IndexClause *
+get_index_clause_from_support(PlannerInfo *root,
+							  RestrictInfo *rinfo,
+							  Oid funcid,
+							  int indexarg,
+							  int indexcol,
+							  IndexOptInfo *index)
+{
+	Oid			prosupport = get_func_support(funcid);
+	SupportRequestIndexCondition req;
+	List	   *sresult;
+
+	if (!OidIsValid(prosupport))
+		return NULL;
+
+	req.type = T_SupportRequestIndexCondition;
+	req.root = root;
+	req.funcid = funcid;
+	req.node = (Node *) rinfo->clause;
+	req.indexarg = indexarg;
+	req.index = index;
+	req.indexcol = indexcol;
+	req.opfamily = index->opfamily[indexcol];
+	req.indexcollation = index->indexcollations[indexcol];
+
+	req.lossy = true;			/* default assumption */
+
+	sresult = (List *)
+		DatumGetPointer(OidFunctionCall1(prosupport,
+										 PointerGetDatum(&req)));
+
+	if (sresult != NIL)
+	{
+		IndexClause *iclause = makeNode(IndexClause);
+		List	   *indexquals = NIL;
+		ListCell   *lc;
+
+		/*
+		 * The support function API says it should just give back bare
+		 * clauses, so here we must wrap each one in a RestrictInfo.
+		 */
+		foreach(lc, sresult)
+		{
+			Expr	   *clause = (Expr *) lfirst(lc);
+
+			indexquals = lappend(indexquals, make_simple_restrictinfo(clause));
+		}
+
+		iclause->rinfo = rinfo;
+		iclause->indexquals = indexquals;
+		iclause->lossy = req.lossy;
+		iclause->indexcol = indexcol;
+		iclause->indexcols = NIL;
+
+		return iclause;
+	}
+
+	return NULL;
+}
+
+/*
+ * match_saopclause_to_indexcol()
+ *	  Handles the ScalarArrayOpExpr case for match_clause_to_indexcol(),
+ *	  which see for comments.
+ */
+static IndexClause *
+match_saopclause_to_indexcol(RestrictInfo *rinfo,
+							 int indexcol,
+							 IndexOptInfo *index)
+{
+	ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) rinfo->clause;
+	Node	   *leftop,
+			   *rightop;
+	Relids		right_relids;
+	Oid			expr_op;
+	Oid			expr_coll;
+	Index		index_relid;
+	Oid			opfamily;
+	Oid			idxcollation;
+
+	/* We only accept ANY clauses, not ALL */
+	if (!saop->useOr)
+		return NULL;
+	leftop = (Node *) linitial(saop->args);
+	rightop = (Node *) lsecond(saop->args);
+	right_relids = pull_varnos(rightop);
+	expr_op = saop->opno;
+	expr_coll = saop->inputcollid;
+
+	index_relid = index->rel->relid;
+	opfamily = index->opfamily[indexcol];
+	idxcollation = index->indexcollations[indexcol];
+
+	/*
+	 * We must have indexkey on the left and a pseudo-constant array argument.
+	 */
+	if (match_index_to_operand(leftop, indexcol, index) &&
+		!bms_is_member(index_relid, right_relids) &&
+		!contain_volatile_functions(rightop))
+	{
+		if (IndexCollMatchesExprColl(idxcollation, expr_coll) &&
+			op_in_opfamily(expr_op, opfamily))
+		{
+			IndexClause *iclause = makeNode(IndexClause);
+
+			iclause->rinfo = rinfo;
+			iclause->indexquals = NIL;
+			iclause->lossy = false;
+			iclause->indexcol = indexcol;
+			iclause->indexcols = NIL;
+			return iclause;
+		}
+
+		/*
+		 * We do not currently ask support functions about ScalarArrayOpExprs,
+		 * though in principle we could.
+		 */
 	}
 
-	/* OK if the (commuted) operator is a member of the index's opfamily */
-	return op_in_opfamily(expr_op, opfamily);
+	return NULL;
 }
 
 /*
  * match_rowcompare_to_indexcol()
  *	  Handles the RowCompareExpr case for match_clause_to_indexcol(),
  *	  which see for comments.
+ *
+ * In this routine we check whether the first column of the row comparison
+ * matches the target index column.  This is sufficient to guarantee that some
+ * index condition can be constructed from the RowCompareExpr --- the rest
+ * is handled by expand_indexqual_rowcompare().
  */
-static bool
-match_rowcompare_to_indexcol(IndexOptInfo *index,
+static IndexClause *
+match_rowcompare_to_indexcol(RestrictInfo *rinfo,
 							 int indexcol,
-							 Oid opfamily,
-							 Oid idxcollation,
-							 RowCompareExpr *clause)
+							 IndexOptInfo *index)
 {
-	Index		index_relid = index->rel->relid;
+	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
+	Index		index_relid;
+	Oid			opfamily;
+	Oid			idxcollation;
 	Node	   *leftop,
 			   *rightop;
+	bool		var_on_left;
 	Oid			expr_op;
 	Oid			expr_coll;
 
 	/* Forget it if we're not dealing with a btree index */
 	if (index->relam != BTREE_AM_OID)
-		return false;
+		return NULL;
+
+	index_relid = index->rel->relid;
+	opfamily = index->opfamily[indexcol];
+	idxcollation = index->indexcollations[indexcol];
 
 	/*
 	 * We could do the matching on the basis of insisting that the opfamily
@@ -2566,16 +2885,17 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
 
 	/* Collations must match, if relevant */
 	if (!IndexCollMatchesExprColl(idxcollation, expr_coll))
-		return false;
+		return NULL;
 
 	/*
-	 * These syntactic tests are the same as in match_clause_to_indexcol()
+	 * These syntactic tests are the same as in match_opclause_to_indexcol()
 	 */
 	if (match_index_to_operand(leftop, indexcol, index) &&
 		!bms_is_member(index_relid, pull_varnos(rightop)) &&
 		!contain_volatile_functions(rightop))
 	{
 		/* OK, indexkey is on left */
+		var_on_left = true;
 	}
 	else if (match_index_to_operand(rightop, indexcol, index) &&
 			 !bms_is_member(index_relid, pull_varnos(leftop)) &&
@@ -2584,10 +2904,11 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
 		/* indexkey is on right, so commute the operator */
 		expr_op = get_commutator(expr_op);
 		if (expr_op == InvalidOid)
-			return false;
+			return NULL;
+		var_on_left = false;
 	}
 	else
-		return false;
+		return NULL;
 
 	/* We're good if the operator is the right type of opfamily member */
 	switch (get_op_opfamily_strategy(expr_op, opfamily))
@@ -2596,26 +2917,266 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
 		case BTLessEqualStrategyNumber:
 		case BTGreaterEqualStrategyNumber:
 		case BTGreaterStrategyNumber:
-			return true;
+			return expand_indexqual_rowcompare(rinfo,
+											   indexcol,
+											   index,
+											   expr_op,
+											   var_on_left);
 	}
 
-	return false;
+	return NULL;
 }
 
-
-/****************************************************************************
- *				----  ROUTINES TO CHECK ORDERING OPERATORS	----
- ****************************************************************************/
-
 /*
- * match_pathkeys_to_index
- *		Test whether an index can produce output ordered according to the
- *		given pathkeys using "ordering operators".
+ * expand_indexqual_rowcompare --- expand a single indexqual condition
+ *		that is a RowCompareExpr
  *
- * If it can, return a list of suitable ORDER BY expressions, each of the form
- * "indexedcol operator pseudoconstant", along with an integer list of the
- * index column numbers (zero based) that each clause would be used with.
- * NIL lists are returned if the ordering is not achievable this way.
+ * It's already known that the first column of the row comparison matches
+ * the specified column of the index.  We can use additional columns of the
+ * row comparison as index qualifications, so long as they match the index
+ * in the "same direction", ie, the indexkeys are all on the same side of the
+ * clause and the operators are all the same-type members of the opfamilies.
+ *
+ * If all the columns of the RowCompareExpr match in this way, we just use it
+ * as-is, except for possibly commuting it to put the indexkeys on the left.
+ *
+ * Otherwise, we build a shortened RowCompareExpr (if more than one
+ * column matches) or a simple OpExpr (if the first-column match is all
+ * there is).  In these cases the modified clause is always "<=" or ">="
+ * even when the original was "<" or ">" --- this is necessary to match all
+ * the rows that could match the original.  (We are building a lossy version
+ * of the row comparison when we do this, so we set lossy = true.)
+ *
+ * Note: this is really just the last half of match_rowcompare_to_indexcol,
+ * but we split it out for comprehensibility.
+ */
+static IndexClause *
+expand_indexqual_rowcompare(RestrictInfo *rinfo,
+							int indexcol,
+							IndexOptInfo *index,
+							Oid expr_op,
+							bool var_on_left)
+{
+	IndexClause *iclause = makeNode(IndexClause);
+	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
+	int			op_strategy;
+	Oid			op_lefttype;
+	Oid			op_righttype;
+	int			matching_cols;
+	List	   *expr_ops;
+	List	   *opfamilies;
+	List	   *lefttypes;
+	List	   *righttypes;
+	List	   *new_ops;
+	List	   *var_args;
+	List	   *non_var_args;
+	ListCell   *vargs_cell;
+	ListCell   *nargs_cell;
+	ListCell   *opnos_cell;
+	ListCell   *collids_cell;
+
+	iclause->rinfo = rinfo;
+	iclause->indexcol = indexcol;
+
+	if (var_on_left)
+	{
+		var_args = clause->largs;
+		non_var_args = clause->rargs;
+	}
+	else
+	{
+		var_args = clause->rargs;
+		non_var_args = clause->largs;
+	}
+
+	get_op_opfamily_properties(expr_op, index->opfamily[indexcol], false,
+							   &op_strategy,
+							   &op_lefttype,
+							   &op_righttype);
+
+	/* Initialize returned list of which index columns are used */
+	iclause->indexcols = list_make1_int(indexcol);
+
+	/* Build lists of ops, opfamilies and operator datatypes in case needed */
+	expr_ops = list_make1_oid(expr_op);
+	opfamilies = list_make1_oid(index->opfamily[indexcol]);
+	lefttypes = list_make1_oid(op_lefttype);
+	righttypes = list_make1_oid(op_righttype);
+
+	/*
+	 * See how many of the remaining columns match some index column in the
+	 * same way.  As in match_clause_to_indexcol(), the "other" side of any
+	 * potential index condition is OK as long as it doesn't use Vars from the
+	 * indexed relation.
+	 */
+	matching_cols = 1;
+	vargs_cell = lnext(list_head(var_args));
+	nargs_cell = lnext(list_head(non_var_args));
+	opnos_cell = lnext(list_head(clause->opnos));
+	collids_cell = lnext(list_head(clause->inputcollids));
+
+	while (vargs_cell != NULL)
+	{
+		Node	   *varop = (Node *) lfirst(vargs_cell);
+		Node	   *constop = (Node *) lfirst(nargs_cell);
+		int			i;
+
+		expr_op = lfirst_oid(opnos_cell);
+		if (!var_on_left)
+		{
+			/* indexkey is on right, so commute the operator */
+			expr_op = get_commutator(expr_op);
+			if (expr_op == InvalidOid)
+				break;			/* operator is not usable */
+		}
+		if (bms_is_member(index->rel->relid, pull_varnos(constop)))
+			break;				/* no good, Var on wrong side */
+		if (contain_volatile_functions(constop))
+			break;				/* no good, volatile comparison value */
+
+		/*
+		 * The Var side can match any column of the index.
+		 */
+		for (i = 0; i < index->nkeycolumns; i++)
+		{
+			if (match_index_to_operand(varop, i, index) &&
+				get_op_opfamily_strategy(expr_op,
+										 index->opfamily[i]) == op_strategy &&
+				IndexCollMatchesExprColl(index->indexcollations[i],
+										 lfirst_oid(collids_cell)))
+				break;
+		}
+		if (i >= index->ncolumns)
+			break;				/* no match found */
+
+		/* Add column number to returned list */
+		iclause->indexcols = lappend_int(iclause->indexcols, i);
+
+		/* Add operator info to lists */
+		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
+								   &op_strategy,
+								   &op_lefttype,
+								   &op_righttype);
+		expr_ops = lappend_oid(expr_ops, expr_op);
+		opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
+		lefttypes = lappend_oid(lefttypes, op_lefttype);
+		righttypes = lappend_oid(righttypes, op_righttype);
+
+		/* This column matches, keep scanning */
+		matching_cols++;
+		vargs_cell = lnext(vargs_cell);
+		nargs_cell = lnext(nargs_cell);
+		opnos_cell = lnext(opnos_cell);
+		collids_cell = lnext(collids_cell);
+	}
+
+	/* Result is non-lossy if all columns are usable as index quals */
+	iclause->lossy = (matching_cols != list_length(clause->opnos));
+
+	/*
+	 * We can use rinfo->clause as-is if we have var on left and it's all
+	 * usable as index quals.
+	 */
+	if (var_on_left && !iclause->lossy)
+		iclause->indexquals = NIL;
+	else
+	{
+		/*
+		 * We have to generate a modified rowcompare (possibly just one
+		 * OpExpr).  The painful part of this is changing < to <= or > to >=,
+		 * so deal with that first.
+		 */
+		if (!iclause->lossy)
+		{
+			/* very easy, just use the commuted operators */
+			new_ops = expr_ops;
+		}
+		else if (op_strategy == BTLessEqualStrategyNumber ||
+				 op_strategy == BTGreaterEqualStrategyNumber)
+		{
+			/* easy, just use the same (possibly commuted) operators */
+			new_ops = list_truncate(expr_ops, matching_cols);
+		}
+		else
+		{
+			ListCell   *opfamilies_cell;
+			ListCell   *lefttypes_cell;
+			ListCell   *righttypes_cell;
+
+			if (op_strategy == BTLessStrategyNumber)
+				op_strategy = BTLessEqualStrategyNumber;
+			else if (op_strategy == BTGreaterStrategyNumber)
+				op_strategy = BTGreaterEqualStrategyNumber;
+			else
+				elog(ERROR, "unexpected strategy number %d", op_strategy);
+			new_ops = NIL;
+			forthree(opfamilies_cell, opfamilies,
+					 lefttypes_cell, lefttypes,
+					 righttypes_cell, righttypes)
+			{
+				Oid			opfam = lfirst_oid(opfamilies_cell);
+				Oid			lefttype = lfirst_oid(lefttypes_cell);
+				Oid			righttype = lfirst_oid(righttypes_cell);
+
+				expr_op = get_opfamily_member(opfam, lefttype, righttype,
+											  op_strategy);
+				if (!OidIsValid(expr_op))	/* should not happen */
+					elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
+						 op_strategy, lefttype, righttype, opfam);
+				new_ops = lappend_oid(new_ops, expr_op);
+			}
+		}
+
+		/* If we have more than one matching col, create a subset rowcompare */
+		if (matching_cols > 1)
+		{
+			RowCompareExpr *rc = makeNode(RowCompareExpr);
+
+			rc->rctype = (RowCompareType) op_strategy;
+			rc->opnos = new_ops;
+			rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
+										   matching_cols);
+			rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
+											 matching_cols);
+			rc->largs = list_truncate(copyObject(var_args),
+									  matching_cols);
+			rc->rargs = list_truncate(copyObject(non_var_args),
+									  matching_cols);
+			iclause->indexquals = list_make1(make_simple_restrictinfo((Expr *) rc));
+		}
+		else
+		{
+			Expr	   *op;
+
+			/* We don't report an index column list in this case */
+			iclause->indexcols = NIL;
+
+			op = make_opclause(linitial_oid(new_ops), BOOLOID, false,
+							   copyObject(linitial(var_args)),
+							   copyObject(linitial(non_var_args)),
+							   InvalidOid,
+							   linitial_oid(clause->inputcollids));
+			iclause->indexquals = list_make1(make_simple_restrictinfo(op));
+		}
+	}
+
+	return iclause;
+}
+
+
+/****************************************************************************
+ *				----  ROUTINES TO CHECK ORDERING OPERATORS	----
+ ****************************************************************************/
+
+/*
+ * match_pathkeys_to_index
+ *		Test whether an index can produce output ordered according to the
+ *		given pathkeys using "ordering operators".
+ *
+ * If it can, return a list of suitable ORDER BY expressions, each of the form
+ * "indexedcol operator pseudoconstant", along with an integer list of the
+ * index column numbers (zero based) that each clause would be used with.
+ * NIL lists are returned if the ordering is not achievable this way.
  *
  * On success, the result list is ordered by pathkeys, and in fact is
  * one-to-one with the requested pathkeys.
@@ -3233,7 +3794,7 @@ indexcol_is_bool_constant_for_query(IndexOptInfo *index, int indexcol)
 			continue;
 
 		/* See if we can match the clause's expression to the index column */
-		if (match_boolean_index_clause((Node *) rinfo->clause, indexcol, index))
+		if (match_boolean_index_clause(rinfo, indexcol, index))
 			return true;
 	}
 
@@ -3323,1057 +3884,3 @@ match_index_to_operand(Node *operand,
 
 	return false;
 }
-
-/****************************************************************************
- *			----  ROUTINES FOR "SPECIAL" INDEXABLE OPERATORS  ----
- ****************************************************************************/
-
-/*
- * These routines handle special optimization of operators that can be
- * used with index scans even though they are not known to the executor's
- * indexscan machinery.  The key idea is that these operators allow us
- * to derive approximate indexscan qual clauses, such that any tuples
- * that pass the operator clause itself must also satisfy the simpler
- * indexscan condition(s).  Then we can use the indexscan machinery
- * to avoid scanning as much of the table as we'd otherwise have to,
- * while applying the original operator as a qpqual condition to ensure
- * we deliver only the tuples we want.  (In essence, we're using a regular
- * index as if it were a lossy index.)
- *
- * An example of what we're doing is
- *			textfield LIKE 'abc%'
- * from which we can generate the indexscanable conditions
- *			textfield >= 'abc' AND textfield < 'abd'
- * which allow efficient scanning of an index on textfield.
- * (In reality, character set and collation issues make the transformation
- * from LIKE to indexscan limits rather harder than one might think ...
- * but that's the basic idea.)
- *
- * Another thing that we do with this machinery is to provide special
- * smarts for "boolean" indexes (that is, indexes on boolean columns
- * that support boolean equality).  We can transform a plain reference
- * to the indexkey into "indexkey = true", or "NOT indexkey" into
- * "indexkey = false", so as to make the expression indexable using the
- * regular index operators.  (As of Postgres 8.1, we must do this here
- * because constant simplification does the reverse transformation;
- * without this code there'd be no way to use such an index at all.)
- *
- * Three routines are provided here:
- *
- * match_special_index_operator() is just an auxiliary function for
- * match_clause_to_indexcol(); after the latter fails to recognize a
- * restriction opclause's operator as a member of an index's opfamily,
- * it asks match_special_index_operator() whether the clause should be
- * considered an indexqual anyway.
- *
- * match_boolean_index_clause() similarly detects clauses that can be
- * converted into boolean equality operators.
- *
- * expand_indexqual_conditions() converts a RestrictInfo node
- * into an IndexClause, which contains clauses
- * that the executor can actually handle.  For operators that are members of
- * the index's opfamily this transformation is a no-op, but clauses recognized
- * by match_special_index_operator() or match_boolean_index_clause() must be
- * converted into one or more "regular" indexqual conditions.
- */
-
-/*
- * match_boolean_index_clause
- *	  Recognize restriction clauses that can be matched to a boolean index.
- *
- * This should be called only when IsBooleanOpfamily() recognizes the
- * index's operator family.  We check to see if the clause matches the
- * index's key.
- */
-static bool
-match_boolean_index_clause(Node *clause,
-						   int indexcol,
-						   IndexOptInfo *index)
-{
-	/* Direct match? */
-	if (match_index_to_operand(clause, indexcol, index))
-		return true;
-	/* NOT clause? */
-	if (is_notclause(clause))
-	{
-		if (match_index_to_operand((Node *) get_notclausearg((Expr *) clause),
-								   indexcol, index))
-			return true;
-	}
-
-	/*
-	 * Since we only consider clauses at top level of WHERE, we can convert
-	 * indexkey IS TRUE and indexkey IS FALSE to index searches as well. The
-	 * different meaning for NULL isn't important.
-	 */
-	else if (clause && IsA(clause, BooleanTest))
-	{
-		BooleanTest *btest = (BooleanTest *) clause;
-
-		if (btest->booltesttype == IS_TRUE ||
-			btest->booltesttype == IS_FALSE)
-			if (match_index_to_operand((Node *) btest->arg,
-									   indexcol, index))
-				return true;
-	}
-	return false;
-}
-
-/*
- * match_special_index_operator
- *	  Recognize restriction clauses that can be used to generate
- *	  additional indexscanable qualifications.
- *
- * The given clause is already known to be a binary opclause having
- * the form (indexkey OP pseudoconst) or (pseudoconst OP indexkey),
- * but the OP proved not to be one of the index's opfamily operators.
- * Return 'true' if we can do something with it anyway.
- */
-static bool
-match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation,
-							 bool indexkey_on_left)
-{
-	bool		isIndexable = false;
-	Node	   *rightop;
-	Oid			expr_op;
-	Oid			expr_coll;
-	Const	   *patt;
-	Const	   *prefix = NULL;
-	Pattern_Prefix_Status pstatus = Pattern_Prefix_None;
-
-	/*
-	 * Currently, all known special operators require the indexkey on the
-	 * left, but this test could be pushed into the switch statement if some
-	 * are added that do not...
-	 */
-	if (!indexkey_on_left)
-		return false;
-
-	/* we know these will succeed */
-	rightop = get_rightop(clause);
-	expr_op = ((OpExpr *) clause)->opno;
-	expr_coll = ((OpExpr *) clause)->inputcollid;
-
-	/* again, required for all current special ops: */
-	if (!IsA(rightop, Const) ||
-		((Const *) rightop)->constisnull)
-		return false;
-	patt = (Const *) rightop;
-
-	switch (expr_op)
-	{
-		case OID_TEXT_LIKE_OP:
-		case OID_BPCHAR_LIKE_OP:
-		case OID_NAME_LIKE_OP:
-			/* the right-hand const is type text for all of these */
-			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll,
-										   &prefix, NULL);
-			isIndexable = (pstatus != Pattern_Prefix_None);
-			break;
-
-		case OID_BYTEA_LIKE_OP:
-			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll,
-										   &prefix, NULL);
-			isIndexable = (pstatus != Pattern_Prefix_None);
-			break;
-
-		case OID_TEXT_ICLIKE_OP:
-		case OID_BPCHAR_ICLIKE_OP:
-		case OID_NAME_ICLIKE_OP:
-			/* the right-hand const is type text for all of these */
-			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, expr_coll,
-										   &prefix, NULL);
-			isIndexable = (pstatus != Pattern_Prefix_None);
-			break;
-
-		case OID_TEXT_REGEXEQ_OP:
-		case OID_BPCHAR_REGEXEQ_OP:
-		case OID_NAME_REGEXEQ_OP:
-			/* the right-hand const is type text for all of these */
-			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, expr_coll,
-										   &prefix, NULL);
-			isIndexable = (pstatus != Pattern_Prefix_None);
-			break;
-
-		case OID_TEXT_ICREGEXEQ_OP:
-		case OID_BPCHAR_ICREGEXEQ_OP:
-		case OID_NAME_ICREGEXEQ_OP:
-			/* the right-hand const is type text for all of these */
-			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, expr_coll,
-										   &prefix, NULL);
-			isIndexable = (pstatus != Pattern_Prefix_None);
-			break;
-
-		case OID_INET_SUB_OP:
-		case OID_INET_SUBEQ_OP:
-			isIndexable = true;
-			break;
-	}
-
-	if (prefix)
-	{
-		pfree(DatumGetPointer(prefix->constvalue));
-		pfree(prefix);
-	}
-
-	/* done if the expression doesn't look indexable */
-	if (!isIndexable)
-		return false;
-
-	/*
-	 * Must also check that index's opfamily supports the operators we will
-	 * want to apply.  (A hash index, for example, will not support ">=".)
-	 * Currently, only btree and spgist support the operators we need.
-	 *
-	 * Note: actually, in the Pattern_Prefix_Exact case, we only need "=" so a
-	 * hash index would work.  Currently it doesn't seem worth checking for
-	 * that, however.
-	 *
-	 * We insist on the opfamily being the specific one we expect, else we'd
-	 * do the wrong thing if someone were to make a reverse-sort opfamily with
-	 * the same operators.
-	 *
-	 * The non-pattern opclasses will not sort the way we need in most non-C
-	 * locales.  We can use such an index anyway for an exact match (simple
-	 * equality), but not for prefix-match cases.  Note that here we are
-	 * looking at the index's collation, not the expression's collation --
-	 * this test is *not* dependent on the LIKE/regex operator's collation.
-	 */
-	switch (expr_op)
-	{
-		case OID_TEXT_LIKE_OP:
-		case OID_TEXT_ICLIKE_OP:
-		case OID_TEXT_REGEXEQ_OP:
-		case OID_TEXT_ICREGEXEQ_OP:
-		case OID_NAME_LIKE_OP:
-		case OID_NAME_ICLIKE_OP:
-		case OID_NAME_REGEXEQ_OP:
-		case OID_NAME_ICREGEXEQ_OP:
-			isIndexable =
-				(opfamily == TEXT_PATTERN_BTREE_FAM_OID) ||
-				(opfamily == TEXT_SPGIST_FAM_OID) ||
-				(opfamily == TEXT_BTREE_FAM_OID &&
-				 (pstatus == Pattern_Prefix_Exact ||
-				  lc_collate_is_c(idxcollation)));
-			break;
-
-		case OID_BPCHAR_LIKE_OP:
-		case OID_BPCHAR_ICLIKE_OP:
-		case OID_BPCHAR_REGEXEQ_OP:
-		case OID_BPCHAR_ICREGEXEQ_OP:
-			isIndexable =
-				(opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) ||
-				(opfamily == BPCHAR_BTREE_FAM_OID &&
-				 (pstatus == Pattern_Prefix_Exact ||
-				  lc_collate_is_c(idxcollation)));
-			break;
-
-		case OID_BYTEA_LIKE_OP:
-			isIndexable = (opfamily == BYTEA_BTREE_FAM_OID);
-			break;
-
-		case OID_INET_SUB_OP:
-		case OID_INET_SUBEQ_OP:
-			isIndexable = (opfamily == NETWORK_BTREE_FAM_OID);
-			break;
-	}
-
-	return isIndexable;
-}
-
-/*
- * expand_indexqual_conditions
- *	  Given a RestrictInfo node, create an IndexClause.
- *
- * Standard qual clauses (those in the index's opfamily) are passed through
- * unchanged.  Boolean clauses and "special" index operators are expanded
- * into clauses that the indexscan machinery will know what to do with.
- * RowCompare clauses are simplified if necessary to create a clause that is
- * fully checkable by the index.
- */
-static IndexClause *
-expand_indexqual_conditions(IndexOptInfo *index,
-							int indexcol,
-							RestrictInfo *rinfo)
-{
-	IndexClause *iclause = makeNode(IndexClause);
-	List	   *indexquals = NIL;
-
-	iclause->rinfo = rinfo;
-	iclause->lossy = false;		/* might get changed below */
-	iclause->indexcol = indexcol;
-	iclause->indexcols = NIL;	/* might get changed below */
-
-	{
-		Expr	   *clause = rinfo->clause;
-		Oid			curFamily;
-		Oid			curCollation;
-
-		Assert(indexcol < index->nkeycolumns);
-
-		curFamily = index->opfamily[indexcol];
-		curCollation = index->indexcollations[indexcol];
-
-		/* First check for boolean cases */
-		if (IsBooleanOpfamily(curFamily))
-		{
-			Expr	   *boolqual;
-
-			boolqual = expand_boolean_index_clause((Node *) clause,
-												   indexcol,
-												   index);
-			if (boolqual)
-			{
-				iclause->indexquals =
-					list_make1(make_simple_restrictinfo(boolqual));
-				return iclause;
-			}
-		}
-
-		/*
-		 * Else it must be an opclause (usual case), ScalarArrayOp,
-		 * RowCompare, or NullTest
-		 */
-		if (is_opclause(clause))
-		{
-			/*
-			 * Check to see if the indexkey is on the right; if so, commute
-			 * the clause.  The indexkey should be the side that refers to
-			 * (only) the base relation.
-			 */
-			if (!bms_equal(rinfo->left_relids, index->rel->relids))
-			{
-				Oid			opno = ((OpExpr *) clause)->opno;
-				RestrictInfo *newrinfo;
-
-				newrinfo = commute_restrictinfo(rinfo,
-												get_commutator(opno));
-
-				/*
-				 * For now, assume it couldn't be any case that requires
-				 * expansion.  (This is OK for the current capabilities of
-				 * expand_indexqual_opclause, but we'll need to remove the
-				 * restriction when we open this up for extensions.)
-				 */
-				indexquals = list_make1(newrinfo);
-			}
-			else
-				indexquals = expand_indexqual_opclause(rinfo,
-													   curFamily,
-													   curCollation,
-													   &iclause->lossy);
-		}
-		else if (IsA(clause, ScalarArrayOpExpr))
-		{
-			/* no extra work at this time */
-		}
-		else if (IsA(clause, RowCompareExpr))
-		{
-			RestrictInfo *newrinfo;
-
-			newrinfo = expand_indexqual_rowcompare(rinfo,
-												   index,
-												   indexcol,
-												   &iclause->indexcols,
-												   &iclause->lossy);
-			if (newrinfo != rinfo)
-			{
-				/* We need to report a derived expression */
-				indexquals = list_make1(newrinfo);
-			}
-		}
-		else if (IsA(clause, NullTest))
-		{
-			Assert(index->amsearchnulls);
-		}
-		else
-			elog(ERROR, "unsupported indexqual type: %d",
-				 (int) nodeTag(clause));
-	}
-
-	iclause->indexquals = indexquals;
-	return iclause;
-}
-
-/*
- * expand_boolean_index_clause
- *	  Convert a clause recognized by match_boolean_index_clause into
- *	  a boolean equality operator clause.
- *
- * Returns NULL if the clause isn't a boolean index qual.
- */
-static Expr *
-expand_boolean_index_clause(Node *clause,
-							int indexcol,
-							IndexOptInfo *index)
-{
-	/* Direct match? */
-	if (match_index_to_operand(clause, indexcol, index))
-	{
-		/* convert to indexkey = TRUE */
-		return make_opclause(BooleanEqualOperator, BOOLOID, false,
-							 (Expr *) clause,
-							 (Expr *) makeBoolConst(true, false),
-							 InvalidOid, InvalidOid);
-	}
-	/* NOT clause? */
-	if (is_notclause(clause))
-	{
-		Node	   *arg = (Node *) get_notclausearg((Expr *) clause);
-
-		/* It must have matched the indexkey */
-		Assert(match_index_to_operand(arg, indexcol, index));
-		/* convert to indexkey = FALSE */
-		return make_opclause(BooleanEqualOperator, BOOLOID, false,
-							 (Expr *) arg,
-							 (Expr *) makeBoolConst(false, false),
-							 InvalidOid, InvalidOid);
-	}
-	if (clause && IsA(clause, BooleanTest))
-	{
-		BooleanTest *btest = (BooleanTest *) clause;
-		Node	   *arg = (Node *) btest->arg;
-
-		/* It must have matched the indexkey */
-		Assert(match_index_to_operand(arg, indexcol, index));
-		if (btest->booltesttype == IS_TRUE)
-		{
-			/* convert to indexkey = TRUE */
-			return make_opclause(BooleanEqualOperator, BOOLOID, false,
-								 (Expr *) arg,
-								 (Expr *) makeBoolConst(true, false),
-								 InvalidOid, InvalidOid);
-		}
-		if (btest->booltesttype == IS_FALSE)
-		{
-			/* convert to indexkey = FALSE */
-			return make_opclause(BooleanEqualOperator, BOOLOID, false,
-								 (Expr *) arg,
-								 (Expr *) makeBoolConst(false, false),
-								 InvalidOid, InvalidOid);
-		}
-		/* Oops */
-		Assert(false);
-	}
-
-	return NULL;
-}
-
-/*
- * expand_indexqual_opclause --- expand a single indexqual condition
- *		that is an operator clause
- *
- * The input is a single RestrictInfo, the output a list of RestrictInfos,
- * or NIL if the RestrictInfo's clause can be used as-is.
- *
- * In the base case this is just "return NIL", but we have to be prepared to
- * expand special cases that were accepted by match_special_index_operator().
- */
-static List *
-expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation,
-						  bool *lossy)
-{
-	Expr	   *clause = rinfo->clause;
-
-	/* we know these will succeed */
-	Node	   *leftop = get_leftop(clause);
-	Node	   *rightop = get_rightop(clause);
-	Oid			expr_op = ((OpExpr *) clause)->opno;
-	Oid			expr_coll = ((OpExpr *) clause)->inputcollid;
-	Const	   *patt = (Const *) rightop;
-	Const	   *prefix = NULL;
-	Pattern_Prefix_Status pstatus;
-
-	/*
-	 * LIKE and regex operators are not members of any btree index opfamily,
-	 * but they can be members of opfamilies for more exotic index types such
-	 * as GIN.  Therefore, we should only do expansion if the operator is
-	 * actually not in the opfamily.  But checking that requires a syscache
-	 * lookup, so it's best to first see if the operator is one we are
-	 * interested in.
-	 */
-	switch (expr_op)
-	{
-		case OID_TEXT_LIKE_OP:
-		case OID_BPCHAR_LIKE_OP:
-		case OID_NAME_LIKE_OP:
-		case OID_BYTEA_LIKE_OP:
-			if (!op_in_opfamily(expr_op, opfamily))
-			{
-				*lossy = true;
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll,
-											   &prefix, NULL);
-				return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
-			}
-			break;
-
-		case OID_TEXT_ICLIKE_OP:
-		case OID_BPCHAR_ICLIKE_OP:
-		case OID_NAME_ICLIKE_OP:
-			if (!op_in_opfamily(expr_op, opfamily))
-			{
-				*lossy = true;
-				/* the right-hand const is type text for all of these */
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, expr_coll,
-											   &prefix, NULL);
-				return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
-			}
-			break;
-
-		case OID_TEXT_REGEXEQ_OP:
-		case OID_BPCHAR_REGEXEQ_OP:
-		case OID_NAME_REGEXEQ_OP:
-			if (!op_in_opfamily(expr_op, opfamily))
-			{
-				*lossy = true;
-				/* the right-hand const is type text for all of these */
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, expr_coll,
-											   &prefix, NULL);
-				return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
-			}
-			break;
-
-		case OID_TEXT_ICREGEXEQ_OP:
-		case OID_BPCHAR_ICREGEXEQ_OP:
-		case OID_NAME_ICREGEXEQ_OP:
-			if (!op_in_opfamily(expr_op, opfamily))
-			{
-				*lossy = true;
-				/* the right-hand const is type text for all of these */
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, expr_coll,
-											   &prefix, NULL);
-				return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
-			}
-			break;
-
-		case OID_INET_SUB_OP:
-		case OID_INET_SUBEQ_OP:
-			if (!op_in_opfamily(expr_op, opfamily))
-			{
-				*lossy = true;
-				return network_prefix_quals(leftop, expr_op, opfamily,
-											patt->constvalue);
-			}
-			break;
-	}
-
-	/* Default case: the clause can be used as-is. */
-	*lossy = false;
-	return NIL;
-}
-
-/*
- * expand_indexqual_rowcompare --- expand a single indexqual condition
- *		that is a RowCompareExpr
- *
- * It's already known that the first column of the row comparison matches
- * the specified column of the index.  We can use additional columns of the
- * row comparison as index qualifications, so long as they match the index
- * in the "same direction", ie, the indexkeys are all on the same side of the
- * clause and the operators are all the same-type members of the opfamilies.
- *
- * If all the columns of the RowCompareExpr match in this way, we just use it
- * as-is, except for possibly commuting it to put the indexkeys on the left.
- *
- * Otherwise, we build a shortened RowCompareExpr (if more than one
- * column matches) or a simple OpExpr (if the first-column match is all
- * there is).  In these cases the modified clause is always "<=" or ">="
- * even when the original was "<" or ">" --- this is necessary to match all
- * the rows that could match the original.  (We are building a lossy version
- * of the row comparison when we do this, so we set *lossy = true.)
- *
- * *indexcolnos receives an integer list of the index column numbers (zero
- * based) used in the resulting expression.  We have to pass that back
- * because createplan.c will need it.
- */
-static RestrictInfo *
-expand_indexqual_rowcompare(RestrictInfo *rinfo,
-							IndexOptInfo *index,
-							int indexcol,
-							List **indexcolnos,
-							bool *lossy)
-{
-	RowCompareExpr *clause = castNode(RowCompareExpr, rinfo->clause);
-	bool		var_on_left;
-	int			op_strategy;
-	Oid			op_lefttype;
-	Oid			op_righttype;
-	int			matching_cols;
-	Oid			expr_op;
-	List	   *expr_ops;
-	List	   *opfamilies;
-	List	   *lefttypes;
-	List	   *righttypes;
-	List	   *new_ops;
-	List	   *var_args;
-	List	   *non_var_args;
-	ListCell   *vargs_cell;
-	ListCell   *nargs_cell;
-	ListCell   *opnos_cell;
-	ListCell   *collids_cell;
-
-	/* We have to figure out (again) how the first col matches */
-	var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
-										 indexcol, index);
-	Assert(var_on_left ||
-		   match_index_to_operand((Node *) linitial(clause->rargs),
-								  indexcol, index));
-
-	if (var_on_left)
-	{
-		var_args = clause->largs;
-		non_var_args = clause->rargs;
-	}
-	else
-	{
-		var_args = clause->rargs;
-		non_var_args = clause->largs;
-	}
-
-	expr_op = linitial_oid(clause->opnos);
-	if (!var_on_left)
-		expr_op = get_commutator(expr_op);
-	get_op_opfamily_properties(expr_op, index->opfamily[indexcol], false,
-							   &op_strategy,
-							   &op_lefttype,
-							   &op_righttype);
-
-	/* Initialize returned list of which index columns are used */
-	*indexcolnos = list_make1_int(indexcol);
-
-	/* Build lists of ops, opfamilies and operator datatypes in case needed */
-	expr_ops = list_make1_oid(expr_op);
-	opfamilies = list_make1_oid(index->opfamily[indexcol]);
-	lefttypes = list_make1_oid(op_lefttype);
-	righttypes = list_make1_oid(op_righttype);
-
-	/*
-	 * See how many of the remaining columns match some index column in the
-	 * same way.  As in match_clause_to_indexcol(), the "other" side of any
-	 * potential index condition is OK as long as it doesn't use Vars from the
-	 * indexed relation.
-	 */
-	matching_cols = 1;
-	vargs_cell = lnext(list_head(var_args));
-	nargs_cell = lnext(list_head(non_var_args));
-	opnos_cell = lnext(list_head(clause->opnos));
-	collids_cell = lnext(list_head(clause->inputcollids));
-
-	while (vargs_cell != NULL)
-	{
-		Node	   *varop = (Node *) lfirst(vargs_cell);
-		Node	   *constop = (Node *) lfirst(nargs_cell);
-		int			i;
-
-		expr_op = lfirst_oid(opnos_cell);
-		if (!var_on_left)
-		{
-			/* indexkey is on right, so commute the operator */
-			expr_op = get_commutator(expr_op);
-			if (expr_op == InvalidOid)
-				break;			/* operator is not usable */
-		}
-		if (bms_is_member(index->rel->relid, pull_varnos(constop)))
-			break;				/* no good, Var on wrong side */
-		if (contain_volatile_functions(constop))
-			break;				/* no good, volatile comparison value */
-
-		/*
-		 * The Var side can match any column of the index.
-		 */
-		for (i = 0; i < index->nkeycolumns; i++)
-		{
-			if (match_index_to_operand(varop, i, index) &&
-				get_op_opfamily_strategy(expr_op,
-										 index->opfamily[i]) == op_strategy &&
-				IndexCollMatchesExprColl(index->indexcollations[i],
-										 lfirst_oid(collids_cell)))
-
-				break;
-		}
-		if (i >= index->ncolumns)
-			break;				/* no match found */
-
-		/* Add column number to returned list */
-		*indexcolnos = lappend_int(*indexcolnos, i);
-
-		/* Add operator info to lists */
-		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
-								   &op_strategy,
-								   &op_lefttype,
-								   &op_righttype);
-		expr_ops = lappend_oid(expr_ops, expr_op);
-		opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
-		lefttypes = lappend_oid(lefttypes, op_lefttype);
-		righttypes = lappend_oid(righttypes, op_righttype);
-
-		/* This column matches, keep scanning */
-		matching_cols++;
-		vargs_cell = lnext(vargs_cell);
-		nargs_cell = lnext(nargs_cell);
-		opnos_cell = lnext(opnos_cell);
-		collids_cell = lnext(collids_cell);
-	}
-
-	/* Result is non-lossy if all columns are usable as index quals */
-	*lossy = (matching_cols != list_length(clause->opnos));
-
-	/*
-	 * Return clause as-is if we have var on left and it's all usable as index
-	 * quals
-	 */
-	if (var_on_left && !*lossy)
-		return rinfo;
-
-	/*
-	 * We have to generate a modified rowcompare (possibly just one OpExpr).
-	 * The painful part of this is changing < to <= or > to >=, so deal with
-	 * that first.
-	 */
-	if (!*lossy)
-	{
-		/* very easy, just use the commuted operators */
-		new_ops = expr_ops;
-	}
-	else if (op_strategy == BTLessEqualStrategyNumber ||
-			 op_strategy == BTGreaterEqualStrategyNumber)
-	{
-		/* easy, just use the same (possibly commuted) operators */
-		new_ops = list_truncate(expr_ops, matching_cols);
-	}
-	else
-	{
-		ListCell   *opfamilies_cell;
-		ListCell   *lefttypes_cell;
-		ListCell   *righttypes_cell;
-
-		if (op_strategy == BTLessStrategyNumber)
-			op_strategy = BTLessEqualStrategyNumber;
-		else if (op_strategy == BTGreaterStrategyNumber)
-			op_strategy = BTGreaterEqualStrategyNumber;
-		else
-			elog(ERROR, "unexpected strategy number %d", op_strategy);
-		new_ops = NIL;
-		forthree(opfamilies_cell, opfamilies,
-				 lefttypes_cell, lefttypes,
-				 righttypes_cell, righttypes)
-		{
-			Oid			opfam = lfirst_oid(opfamilies_cell);
-			Oid			lefttype = lfirst_oid(lefttypes_cell);
-			Oid			righttype = lfirst_oid(righttypes_cell);
-
-			expr_op = get_opfamily_member(opfam, lefttype, righttype,
-										  op_strategy);
-			if (!OidIsValid(expr_op))	/* should not happen */
-				elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
-					 op_strategy, lefttype, righttype, opfam);
-			new_ops = lappend_oid(new_ops, expr_op);
-		}
-	}
-
-	/* If we have more than one matching col, create a subset rowcompare */
-	if (matching_cols > 1)
-	{
-		RowCompareExpr *rc = makeNode(RowCompareExpr);
-
-		rc->rctype = (RowCompareType) op_strategy;
-		rc->opnos = new_ops;
-		rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
-									   matching_cols);
-		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
-										 matching_cols);
-		rc->largs = list_truncate(copyObject(var_args),
-								  matching_cols);
-		rc->rargs = list_truncate(copyObject(non_var_args),
-								  matching_cols);
-		return make_simple_restrictinfo((Expr *) rc);
-	}
-	else
-	{
-		Expr	   *op;
-
-		/* We don't report an index column list in this case */
-		*indexcolnos = NIL;
-
-		op = make_opclause(linitial_oid(new_ops), BOOLOID, false,
-						   copyObject(linitial(var_args)),
-						   copyObject(linitial(non_var_args)),
-						   InvalidOid,
-						   linitial_oid(clause->inputcollids));
-		return make_simple_restrictinfo(op);
-	}
-}
-
-/*
- * Given a fixed prefix that all the "leftop" values must have,
- * generate suitable indexqual condition(s).  opfamily is the index
- * operator family; we use it to deduce the appropriate comparison
- * operators and operand datatypes.  collation is the input collation to use.
- */
-static List *
-prefix_quals(Node *leftop, Oid opfamily, Oid collation,
-			 Const *prefix_const, Pattern_Prefix_Status pstatus)
-{
-	List	   *result;
-	Oid			ldatatype = exprType(leftop);
-	Oid			rdatatype;
-	Oid			oproid;
-	Expr	   *expr;
-	FmgrInfo	ltproc;
-	Const	   *greaterstr;
-
-	Assert(pstatus != Pattern_Prefix_None);
-
-	switch (opfamily)
-	{
-		case TEXT_BTREE_FAM_OID:
-		case TEXT_PATTERN_BTREE_FAM_OID:
-		case TEXT_SPGIST_FAM_OID:
-			rdatatype = TEXTOID;
-			break;
-
-		case BPCHAR_BTREE_FAM_OID:
-		case BPCHAR_PATTERN_BTREE_FAM_OID:
-			rdatatype = BPCHAROID;
-			break;
-
-		case BYTEA_BTREE_FAM_OID:
-			rdatatype = BYTEAOID;
-			break;
-
-		default:
-			/* shouldn't get here */
-			elog(ERROR, "unexpected opfamily: %u", opfamily);
-			return NIL;
-	}
-
-	/*
-	 * If necessary, coerce the prefix constant to the right type. The given
-	 * prefix constant is either text or bytea type.
-	 */
-	if (prefix_const->consttype != rdatatype)
-	{
-		char	   *prefix;
-
-		switch (prefix_const->consttype)
-		{
-			case TEXTOID:
-				prefix = TextDatumGetCString(prefix_const->constvalue);
-				break;
-			case BYTEAOID:
-				prefix = DatumGetCString(DirectFunctionCall1(byteaout,
-															 prefix_const->constvalue));
-				break;
-			default:
-				elog(ERROR, "unexpected const type: %u",
-					 prefix_const->consttype);
-				return NIL;
-		}
-		prefix_const = string_to_const(prefix, rdatatype);
-		pfree(prefix);
-	}
-
-	/*
-	 * If we found an exact-match pattern, generate an "=" indexqual.
-	 */
-	if (pstatus == Pattern_Prefix_Exact)
-	{
-		oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
-									 BTEqualStrategyNumber);
-		if (oproid == InvalidOid)
-			elog(ERROR, "no = operator for opfamily %u", opfamily);
-		expr = make_opclause(oproid, BOOLOID, false,
-							 (Expr *) leftop, (Expr *) prefix_const,
-							 InvalidOid, collation);
-		result = list_make1(make_simple_restrictinfo(expr));
-		return result;
-	}
-
-	/*
-	 * Otherwise, we have a nonempty required prefix of the values.
-	 *
-	 * We can always say "x >= prefix".
-	 */
-	oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
-								 BTGreaterEqualStrategyNumber);
-	if (oproid == InvalidOid)
-		elog(ERROR, "no >= operator for opfamily %u", opfamily);
-	expr = make_opclause(oproid, BOOLOID, false,
-						 (Expr *) leftop, (Expr *) prefix_const,
-						 InvalidOid, collation);
-	result = list_make1(make_simple_restrictinfo(expr));
-
-	/*-------
-	 * If we can create a string larger than the prefix, we can say
-	 * "x < greaterstr".  NB: we rely on make_greater_string() to generate
-	 * a guaranteed-greater string, not just a probably-greater string.
-	 * In general this is only guaranteed in C locale, so we'd better be
-	 * using a C-locale index collation.
-	 *-------
-	 */
-	oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
-								 BTLessStrategyNumber);
-	if (oproid == InvalidOid)
-		elog(ERROR, "no < operator for opfamily %u", opfamily);
-	fmgr_info(get_opcode(oproid), &ltproc);
-	greaterstr = make_greater_string(prefix_const, &ltproc, collation);
-	if (greaterstr)
-	{
-		expr = make_opclause(oproid, BOOLOID, false,
-							 (Expr *) leftop, (Expr *) greaterstr,
-							 InvalidOid, collation);
-		result = lappend(result, make_simple_restrictinfo(expr));
-	}
-
-	return result;
-}
-
-/*
- * Given a leftop and a rightop, and an inet-family sup/sub operator,
- * generate suitable indexqual condition(s).  expr_op is the original
- * operator, and opfamily is the index opfamily.
- */
-static List *
-network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop)
-{
-	bool		is_eq;
-	Oid			datatype;
-	Oid			opr1oid;
-	Oid			opr2oid;
-	Datum		opr1right;
-	Datum		opr2right;
-	List	   *result;
-	Expr	   *expr;
-
-	switch (expr_op)
-	{
-		case OID_INET_SUB_OP:
-			datatype = INETOID;
-			is_eq = false;
-			break;
-		case OID_INET_SUBEQ_OP:
-			datatype = INETOID;
-			is_eq = true;
-			break;
-		default:
-			elog(ERROR, "unexpected operator: %u", expr_op);
-			return NIL;
-	}
-
-	/*
-	 * create clause "key >= network_scan_first( rightop )", or ">" if the
-	 * operator disallows equality.
-	 */
-	if (is_eq)
-	{
-		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
-									  BTGreaterEqualStrategyNumber);
-		if (opr1oid == InvalidOid)
-			elog(ERROR, "no >= operator for opfamily %u", opfamily);
-	}
-	else
-	{
-		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
-									  BTGreaterStrategyNumber);
-		if (opr1oid == InvalidOid)
-			elog(ERROR, "no > operator for opfamily %u", opfamily);
-	}
-
-	opr1right = network_scan_first(rightop);
-
-	expr = make_opclause(opr1oid, BOOLOID, false,
-						 (Expr *) leftop,
-						 (Expr *) makeConst(datatype, -1,
-											InvalidOid, /* not collatable */
-											-1, opr1right,
-											false, false),
-						 InvalidOid, InvalidOid);
-	result = list_make1(make_simple_restrictinfo(expr));
-
-	/* create clause "key <= network_scan_last( rightop )" */
-
-	opr2oid = get_opfamily_member(opfamily, datatype, datatype,
-								  BTLessEqualStrategyNumber);
-	if (opr2oid == InvalidOid)
-		elog(ERROR, "no <= operator for opfamily %u", opfamily);
-
-	opr2right = network_scan_last(rightop);
-
-	expr = make_opclause(opr2oid, BOOLOID, false,
-						 (Expr *) leftop,
-						 (Expr *) makeConst(datatype, -1,
-											InvalidOid, /* not collatable */
-											-1, opr2right,
-											false, false),
-						 InvalidOid, InvalidOid);
-	result = lappend(result, make_simple_restrictinfo(expr));
-
-	return result;
-}
-
-/*
- * Handy subroutines for match_special_index_operator() and friends.
- */
-
-/*
- * Generate a Datum of the appropriate type from a C string.
- * Note that all of the supported types are pass-by-ref, so the
- * returned value should be pfree'd if no longer needed.
- */
-static Datum
-string_to_datum(const char *str, Oid datatype)
-{
-	/*
-	 * We cheat a little by assuming that CStringGetTextDatum() will do for
-	 * bpchar and varchar constants too...
-	 */
-	if (datatype == NAMEOID)
-		return DirectFunctionCall1(namein, CStringGetDatum(str));
-	else if (datatype == BYTEAOID)
-		return DirectFunctionCall1(byteain, CStringGetDatum(str));
-	else
-		return CStringGetTextDatum(str);
-}
-
-/*
- * Generate a Const node of the appropriate type from a C string.
- */
-static Const *
-string_to_const(const char *str, Oid datatype)
-{
-	Datum		conval = string_to_datum(str, datatype);
-	Oid			collation;
-	int			constlen;
-
-	/*
-	 * We only need to support a few datatypes here, so hard-wire properties
-	 * instead of incurring the expense of catalog lookups.
-	 */
-	switch (datatype)
-	{
-		case TEXTOID:
-		case VARCHAROID:
-		case BPCHAROID:
-			collation = DEFAULT_COLLATION_OID;
-			constlen = -1;
-			break;
-
-		case NAMEOID:
-			collation = C_COLLATION_OID;
-			constlen = NAMEDATALEN;
-			break;
-
-		case BYTEAOID:
-			collation = InvalidOid;
-			constlen = -1;
-			break;
-
-		default:
-			elog(ERROR, "unexpected datatype in string_to_const: %u",
-				 datatype);
-			return NULL;
-	}
-
-	return makeConst(datatype, -1, collation, constlen,
-					 conval, false, false);
-}
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 20eead1..53e1bf6 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -17,7 +17,8 @@ OBJS = acl.o amutils.o arrayfuncs.o array_expanded.o array_selfuncs.o \
 	float.o format_type.o formatting.o genfile.o \
 	geo_ops.o geo_selfuncs.o geo_spgist.o inet_cidr_ntop.o inet_net_pton.o \
 	int.o int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o \
-	jsonfuncs.o like.o lockfuncs.o mac.o mac8.o misc.o name.o \
+	jsonfuncs.o like.o likesupport.o lockfuncs.o \
+	mac.o mac8.o misc.o name.o \
 	network.o network_gist.o network_selfuncs.o network_spgist.o \
 	numeric.o numutils.o oid.o oracle_compat.o \
 	orderedsetaggs.o partitionfuncs.o pg_locale.o pg_lsn.o \
diff --git a/src/backend/utils/adt/likesupport.c b/src/backend/utils/adt/likesupport.c
index e69de29..fa36310 100644
--- a/src/backend/utils/adt/likesupport.c
+++ b/src/backend/utils/adt/likesupport.c
@@ -0,0 +1,516 @@
+/*-------------------------------------------------------------------------
+ *
+ * likesupport.c
+ *	  Planner support functions for LIKE, regex, and related operators.
+ *
+ * These routines handle special optimization of operators that can be
+ * used with index scans even though they are not known to the executor's
+ * indexscan machinery.  The key idea is that these operators allow us
+ * to derive approximate indexscan qual clauses, such that any tuples
+ * that pass the operator clause itself must also satisfy the simpler
+ * indexscan condition(s).  Then we can use the indexscan machinery
+ * to avoid scanning as much of the table as we'd otherwise have to,
+ * while applying the original operator as a qpqual condition to ensure
+ * we deliver only the tuples we want.  (In essence, we're using a regular
+ * index as if it were a lossy index.)
+ *
+ * An example of what we're doing is
+ *			textfield LIKE 'abc%'
+ * from which we can generate the indexscanable conditions
+ *			textfield >= 'abc' AND textfield < 'abd'
+ * which allow efficient scanning of an index on textfield.
+ * (In reality, character set and collation issues make the transformation
+ * from LIKE to indexscan limits rather harder than one might think ...
+ * but that's the basic idea.)
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/adt/likesupport.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/stratnum.h"
+#include "catalog/pg_opfamily.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/supportnodes.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/pg_locale.h"
+#include "utils/selfuncs.h"
+
+
+static Node *like_regex_support(Node *rawreq, Pattern_Type ptype);
+static List *match_pattern_prefix(Node *leftop,
+					 Node *rightop,
+					 Pattern_Type ptype,
+					 Oid expr_coll,
+					 Oid opfamily,
+					 Oid indexcollation);
+static List *match_network_function(Node *leftop,
+					   Node *rightop,
+					   int indexarg,
+					   Oid funcid,
+					   Oid opfamily);
+static List *match_network_subset(Node *leftop,
+					 Node *rightop,
+					 bool is_eq,
+					 Oid opfamily);
+
+
+/*
+ * Planner support functions for LIKE, regex, and related operators
+ */
+Datum
+textlike_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_POINTER(like_regex_support(rawreq, Pattern_Type_Like));
+}
+
+Datum
+texticlike_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_POINTER(like_regex_support(rawreq, Pattern_Type_Like_IC));
+}
+
+Datum
+textregexeq_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_POINTER(like_regex_support(rawreq, Pattern_Type_Regex));
+}
+
+Datum
+texticregexeq_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+
+	PG_RETURN_POINTER(like_regex_support(rawreq, Pattern_Type_Regex_IC));
+}
+
+/* Common code for the above */
+static Node *
+like_regex_support(Node *rawreq, Pattern_Type ptype)
+{
+	Node	   *ret = NULL;
+
+	if (IsA(rawreq, SupportRequestIndexCondition))
+	{
+		/* Try to convert operator/function call to index conditions */
+		SupportRequestIndexCondition *req = (SupportRequestIndexCondition *) rawreq;
+
+		/*
+		 * Currently we have no "reverse" match operators with the pattern on
+		 * the left, so we only need consider cases with the indexkey on the
+		 * left.
+		 */
+		if (req->indexarg != 0)
+			return NULL;
+
+		if (is_opclause(req->node))
+		{
+			OpExpr	   *clause = (OpExpr *) req->node;
+
+			Assert(list_length(clause->args) == 2);
+			ret = (Node *)
+				match_pattern_prefix((Node *) linitial(clause->args),
+									 (Node *) lsecond(clause->args),
+									 ptype,
+									 clause->inputcollid,
+									 req->opfamily,
+									 req->indexcollation);
+		}
+		else if (is_funcclause(req->node))	/* be paranoid */
+		{
+			FuncExpr   *clause = (FuncExpr *) req->node;
+
+			Assert(list_length(clause->args) == 2);
+			ret = (Node *)
+				match_pattern_prefix((Node *) linitial(clause->args),
+									 (Node *) lsecond(clause->args),
+									 ptype,
+									 clause->inputcollid,
+									 req->opfamily,
+									 req->indexcollation);
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Planner support function for network subset/superset operators
+ */
+Datum
+network_subset_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *ret = NULL;
+
+	if (IsA(rawreq, SupportRequestIndexCondition))
+	{
+		/* Try to convert operator/function call to index conditions */
+		SupportRequestIndexCondition *req = (SupportRequestIndexCondition *) rawreq;
+
+		if (is_opclause(req->node))
+		{
+			OpExpr	   *clause = (OpExpr *) req->node;
+
+			Assert(list_length(clause->args) == 2);
+			ret = (Node *)
+				match_network_function((Node *) linitial(clause->args),
+									   (Node *) lsecond(clause->args),
+									   req->indexarg,
+									   req->funcid,
+									   req->opfamily);
+		}
+		else if (is_funcclause(req->node))	/* be paranoid */
+		{
+			FuncExpr   *clause = (FuncExpr *) req->node;
+
+			Assert(list_length(clause->args) == 2);
+			ret = (Node *)
+				match_network_function((Node *) linitial(clause->args),
+									   (Node *) lsecond(clause->args),
+									   req->indexarg,
+									   req->funcid,
+									   req->opfamily);
+		}
+	}
+
+	PG_RETURN_POINTER(ret);
+}
+
+
+/*
+ * match_pattern_prefix
+ *	  Try to generate an indexqual for a LIKE or regex operator.
+ */
+static List *
+match_pattern_prefix(Node *leftop,
+					 Node *rightop,
+					 Pattern_Type ptype,
+					 Oid expr_coll,
+					 Oid opfamily,
+					 Oid indexcollation)
+{
+	List	   *result;
+	Const	   *patt;
+	Const	   *prefix;
+	Pattern_Prefix_Status pstatus;
+	Oid			ldatatype;
+	Oid			rdatatype;
+	Oid			oproid;
+	Expr	   *expr;
+	FmgrInfo	ltproc;
+	Const	   *greaterstr;
+
+	/*
+	 * Can't do anything with a non-constant or NULL pattern argument.
+	 *
+	 * Note that since we restrict ourselves to cases with a hard constant on
+	 * the RHS, it's a-fortiori a pseudoconstant, and we don't need to worry
+	 * about verifying that.
+	 */
+	if (!IsA(rightop, Const) ||
+		((Const *) rightop)->constisnull)
+		return NIL;
+	patt = (Const *) rightop;
+
+	/*
+	 * Try to extract a fixed prefix from the pattern.
+	 */
+	pstatus = pattern_fixed_prefix(patt, ptype, expr_coll,
+								   &prefix, NULL);
+
+	/* fail if no fixed prefix */
+	if (pstatus == Pattern_Prefix_None)
+		return NIL;
+
+	/*
+	 * Must also check that index's opfamily supports the operators we will
+	 * want to apply.  (A hash index, for example, will not support ">=".)
+	 * Currently, only btree and spgist support the operators we need.
+	 *
+	 * Note: actually, in the Pattern_Prefix_Exact case, we only need "=" so a
+	 * hash index would work.  Currently it doesn't seem worth checking for
+	 * that, however.
+	 *
+	 * We insist on the opfamily being one of the specific ones we expect,
+	 * else we'd do the wrong thing if someone were to make a reverse-sort
+	 * opfamily with the same operators.
+	 *
+	 * The non-pattern opclasses will not sort the way we need in most non-C
+	 * locales.  We can use such an index anyway for an exact match (simple
+	 * equality), but not for prefix-match cases.  Note that here we are
+	 * looking at the index's collation, not the expression's collation --
+	 * this test is *not* dependent on the LIKE/regex operator's collation.
+	 *
+	 * While we're at it, identify the type the comparison constant(s) should
+	 * have, based on the opfamily.
+	 */
+	switch (opfamily)
+	{
+		case TEXT_BTREE_FAM_OID:
+			if (!(pstatus == Pattern_Prefix_Exact ||
+				  lc_collate_is_c(indexcollation)))
+				return NIL;
+			rdatatype = TEXTOID;
+			break;
+
+		case TEXT_PATTERN_BTREE_FAM_OID:
+		case TEXT_SPGIST_FAM_OID:
+			rdatatype = TEXTOID;
+			break;
+
+		case BPCHAR_BTREE_FAM_OID:
+			if (!(pstatus == Pattern_Prefix_Exact ||
+				  lc_collate_is_c(indexcollation)))
+				return NIL;
+			rdatatype = BPCHAROID;
+			break;
+
+		case BPCHAR_PATTERN_BTREE_FAM_OID:
+			rdatatype = BPCHAROID;
+			break;
+
+		case BYTEA_BTREE_FAM_OID:
+			rdatatype = BYTEAOID;
+			break;
+
+		default:
+			return NIL;
+	}
+
+	/* OK, prepare to create the indexqual(s) */
+	ldatatype = exprType(leftop);
+
+	/*
+	 * If necessary, coerce the prefix constant to the right type.  The given
+	 * prefix constant is either text or bytea type, therefore the only case
+	 * where we need to do anything is when converting text to bpchar.  Those
+	 * two types are binary-compatible, so relabeling the Const node is
+	 * sufficient.
+	 */
+	if (prefix->consttype != rdatatype)
+	{
+		Assert(prefix->consttype == TEXTOID &&
+			   rdatatype == BPCHAROID);
+		prefix->consttype = rdatatype;
+	}
+
+	/*
+	 * If we found an exact-match pattern, generate an "=" indexqual.
+	 */
+	if (pstatus == Pattern_Prefix_Exact)
+	{
+		oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
+									 BTEqualStrategyNumber);
+		if (oproid == InvalidOid)
+			elog(ERROR, "no = operator for opfamily %u", opfamily);
+		expr = make_opclause(oproid, BOOLOID, false,
+							 (Expr *) leftop, (Expr *) prefix,
+							 InvalidOid, indexcollation);
+		result = list_make1(expr);
+		return result;
+	}
+
+	/*
+	 * Otherwise, we have a nonempty required prefix of the values.
+	 *
+	 * We can always say "x >= prefix".
+	 */
+	oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
+								 BTGreaterEqualStrategyNumber);
+	if (oproid == InvalidOid)
+		elog(ERROR, "no >= operator for opfamily %u", opfamily);
+	expr = make_opclause(oproid, BOOLOID, false,
+						 (Expr *) leftop, (Expr *) prefix,
+						 InvalidOid, indexcollation);
+	result = list_make1(expr);
+
+	/*-------
+	 * If we can create a string larger than the prefix, we can say
+	 * "x < greaterstr".  NB: we rely on make_greater_string() to generate
+	 * a guaranteed-greater string, not just a probably-greater string.
+	 * In general this is only guaranteed in C locale, so we'd better be
+	 * using a C-locale index collation.
+	 *-------
+	 */
+	oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
+								 BTLessStrategyNumber);
+	if (oproid == InvalidOid)
+		elog(ERROR, "no < operator for opfamily %u", opfamily);
+	fmgr_info(get_opcode(oproid), &ltproc);
+	greaterstr = make_greater_string(prefix, &ltproc, indexcollation);
+	if (greaterstr)
+	{
+		expr = make_opclause(oproid, BOOLOID, false,
+							 (Expr *) leftop, (Expr *) greaterstr,
+							 InvalidOid, indexcollation);
+		result = lappend(result, expr);
+	}
+
+	return result;
+}
+
+
+/*
+ * match_network_function
+ *	  Try to generate an indexqual for a network subset/superset function.
+ *
+ * This layer is just concerned with identifying the function and swapping
+ * the arguments if necessary.
+ */
+static List *
+match_network_function(Node *leftop,
+					   Node *rightop,
+					   int indexarg,
+					   Oid funcid,
+					   Oid opfamily)
+{
+	switch (funcid)
+	{
+		case F_NETWORK_SUB:
+			/* indexkey must be on the left */
+			if (indexarg != 0)
+				return NIL;
+			return match_network_subset(leftop, rightop, false, opfamily);
+
+		case F_NETWORK_SUBEQ:
+			/* indexkey must be on the left */
+			if (indexarg != 0)
+				return NIL;
+			return match_network_subset(leftop, rightop, true, opfamily);
+
+		case F_NETWORK_SUP:
+			/* indexkey must be on the right */
+			if (indexarg != 1)
+				return NIL;
+			return match_network_subset(rightop, leftop, false, opfamily);
+
+		case F_NETWORK_SUPEQ:
+			/* indexkey must be on the right */
+			if (indexarg != 1)
+				return NIL;
+			return match_network_subset(rightop, leftop, true, opfamily);
+
+		default:
+
+			/*
+			 * We'd only get here if somebody attached this support function
+			 * to an unexpected function.  Maybe we should complain, but for
+			 * now, do nothing.
+			 */
+			return NIL;
+	}
+}
+
+/*
+ * match_network_subset
+ *	  Try to generate an indexqual for a network subset function.
+ */
+static List *
+match_network_subset(Node *leftop,
+					 Node *rightop,
+					 bool is_eq,
+					 Oid opfamily)
+{
+	List	   *result;
+	Datum		rightopval;
+	Oid			datatype = INETOID;
+	Oid			opr1oid;
+	Oid			opr2oid;
+	Datum		opr1right;
+	Datum		opr2right;
+	Expr	   *expr;
+
+	/*
+	 * Can't do anything with a non-constant or NULL comparison value.
+	 *
+	 * Note that since we restrict ourselves to cases with a hard constant on
+	 * the RHS, it's a-fortiori a pseudoconstant, and we don't need to worry
+	 * about verifying that.
+	 */
+	if (!IsA(rightop, Const) ||
+		((Const *) rightop)->constisnull)
+		return NIL;
+	rightopval = ((Const *) rightop)->constvalue;
+
+	/*
+	 * Must check that index's opfamily supports the operators we will want to
+	 * apply.
+	 *
+	 * We insist on the opfamily being the specific one we expect, else we'd
+	 * do the wrong thing if someone were to make a reverse-sort opfamily with
+	 * the same operators.
+	 */
+	if (opfamily != NETWORK_BTREE_FAM_OID)
+		return NIL;
+
+	/*
+	 * create clause "key >= network_scan_first( rightopval )", or ">" if the
+	 * operator disallows equality.
+	 *
+	 * Note: seeing that this function supports only fixed values for opfamily
+	 * and datatype, we could just hard-wire the operator OIDs instead of
+	 * looking them up.  But for now it seems better to be general.
+	 */
+	if (is_eq)
+	{
+		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
+									  BTGreaterEqualStrategyNumber);
+		if (opr1oid == InvalidOid)
+			elog(ERROR, "no >= operator for opfamily %u", opfamily);
+	}
+	else
+	{
+		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
+									  BTGreaterStrategyNumber);
+		if (opr1oid == InvalidOid)
+			elog(ERROR, "no > operator for opfamily %u", opfamily);
+	}
+
+	opr1right = network_scan_first(rightopval);
+
+	expr = make_opclause(opr1oid, BOOLOID, false,
+						 (Expr *) leftop,
+						 (Expr *) makeConst(datatype, -1,
+											InvalidOid, /* not collatable */
+											-1, opr1right,
+											false, false),
+						 InvalidOid, InvalidOid);
+	result = list_make1(expr);
+
+	/* create clause "key <= network_scan_last( rightopval )" */
+
+	opr2oid = get_opfamily_member(opfamily, datatype, datatype,
+								  BTLessEqualStrategyNumber);
+	if (opr2oid == InvalidOid)
+		elog(ERROR, "no <= operator for opfamily %u", opfamily);
+
+	opr2right = network_scan_last(rightopval);
+
+	expr = make_opclause(opr2oid, BOOLOID, false,
+						 (Expr *) leftop,
+						 (Expr *) makeConst(datatype, -1,
+											InvalidOid, /* not collatable */
+											-1, opr2right,
+											false, false),
+						 InvalidOid, InvalidOid);
+	result = lappend(result, expr);
+
+	return result;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 221ffbd..b8dede6 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -189,17 +189,21 @@
   prosrc => 'i4tochar' },
 
 { oid => '79',
-  proname => 'nameregexeq', prorettype => 'bool', proargtypes => 'name text',
-  prosrc => 'nameregexeq' },
+  proname => 'nameregexeq', prosupport => 'textregexeq_support',
+  prorettype => 'bool', proargtypes => 'name text', prosrc => 'nameregexeq' },
 { oid => '1252',
   proname => 'nameregexne', prorettype => 'bool', proargtypes => 'name text',
   prosrc => 'nameregexne' },
 { oid => '1254',
-  proname => 'textregexeq', prorettype => 'bool', proargtypes => 'text text',
-  prosrc => 'textregexeq' },
+  proname => 'textregexeq', prosupport => 'textregexeq_support',
+  prorettype => 'bool', proargtypes => 'text text', prosrc => 'textregexeq' },
 { oid => '1256',
   proname => 'textregexne', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'textregexne' },
+{ oid => '1364', descr => 'planner support for textregexeq',
+  proname => 'textregexeq_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'textregexeq_support' },
+
 { oid => '1257', descr => 'length',
   proname => 'textlen', prorettype => 'int4', proargtypes => 'text',
   prosrc => 'textlen' },
@@ -1637,8 +1641,11 @@
   proname => 'position', prorettype => 'int4', proargtypes => 'text text',
   prosrc => 'textpos' },
 { oid => '850',
-  proname => 'textlike', prorettype => 'bool', proargtypes => 'text text',
-  prosrc => 'textlike' },
+  proname => 'textlike', prosupport => 'textlike_support', prorettype => 'bool',
+  proargtypes => 'text text', prosrc => 'textlike' },
+{ oid => '1023', descr => 'planner support for textlike',
+  proname => 'textlike_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'textlike_support' },
 { oid => '851',
   proname => 'textnlike', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'textnlike' },
@@ -1663,8 +1670,8 @@
   proargtypes => 'int4 int8', prosrc => 'int48ge' },
 
 { oid => '858',
-  proname => 'namelike', prorettype => 'bool', proargtypes => 'name text',
-  prosrc => 'namelike' },
+  proname => 'namelike', prosupport => 'textlike_support', prorettype => 'bool',
+  proargtypes => 'name text', prosrc => 'namelike' },
 { oid => '859',
   proname => 'namenlike', prorettype => 'bool', proargtypes => 'name text',
   prosrc => 'namenlike' },
@@ -2354,14 +2361,17 @@
   prosrc => 'int8smaller' },
 
 { oid => '1238',
-  proname => 'texticregexeq', prorettype => 'bool', proargtypes => 'text text',
-  prosrc => 'texticregexeq' },
+  proname => 'texticregexeq', prosupport => 'texticregexeq_support',
+  prorettype => 'bool', proargtypes => 'text text', prosrc => 'texticregexeq' },
+{ oid => '1024', descr => 'planner support for texticregexeq',
+  proname => 'texticregexeq_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'texticregexeq_support' },
 { oid => '1239',
   proname => 'texticregexne', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'texticregexne' },
 { oid => '1240',
-  proname => 'nameicregexeq', prorettype => 'bool', proargtypes => 'name text',
-  prosrc => 'nameicregexeq' },
+  proname => 'nameicregexeq', prosupport => 'texticregexeq_support',
+  prorettype => 'bool', proargtypes => 'name text', prosrc => 'nameicregexeq' },
 { oid => '1241',
   proname => 'nameicregexne', prorettype => 'bool', proargtypes => 'name text',
   prosrc => 'nameicregexne' },
@@ -3130,14 +3140,14 @@
   prosrc => 'bittypmodout' },
 
 { oid => '1569', descr => 'matches LIKE expression',
-  proname => 'like', prorettype => 'bool', proargtypes => 'text text',
-  prosrc => 'textlike' },
+  proname => 'like', prosupport => 'textlike_support', prorettype => 'bool',
+  proargtypes => 'text text', prosrc => 'textlike' },
 { oid => '1570', descr => 'does not match LIKE expression',
   proname => 'notlike', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'textnlike' },
 { oid => '1571', descr => 'matches LIKE expression',
-  proname => 'like', prorettype => 'bool', proargtypes => 'name text',
-  prosrc => 'namelike' },
+  proname => 'like', prosupport => 'textlike_support', prorettype => 'bool',
+  proargtypes => 'name text', prosrc => 'namelike' },
 { oid => '1572', descr => 'does not match LIKE expression',
   proname => 'notlike', prorettype => 'bool', proargtypes => 'name text',
   prosrc => 'namenlike' },
@@ -3301,21 +3311,24 @@
   proargtypes => 'float8 interval', prosrc => 'mul_d_interval' },
 
 { oid => '1631',
-  proname => 'bpcharlike', prorettype => 'bool', proargtypes => 'bpchar text',
-  prosrc => 'textlike' },
+  proname => 'bpcharlike', prosupport => 'textlike_support',
+  prorettype => 'bool', proargtypes => 'bpchar text', prosrc => 'textlike' },
 { oid => '1632',
   proname => 'bpcharnlike', prorettype => 'bool', proargtypes => 'bpchar text',
   prosrc => 'textnlike' },
 
 { oid => '1633',
-  proname => 'texticlike', prorettype => 'bool', proargtypes => 'text text',
-  prosrc => 'texticlike' },
+  proname => 'texticlike', prosupport => 'texticlike_support',
+  prorettype => 'bool', proargtypes => 'text text', prosrc => 'texticlike' },
+{ oid => '1025', descr => 'planner support for texticlike',
+  proname => 'texticlike_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'texticlike_support' },
 { oid => '1634',
   proname => 'texticnlike', prorettype => 'bool', proargtypes => 'text text',
   prosrc => 'texticnlike' },
 { oid => '1635',
-  proname => 'nameiclike', prorettype => 'bool', proargtypes => 'name text',
-  prosrc => 'nameiclike' },
+  proname => 'nameiclike', prosupport => 'texticlike_support',
+  prorettype => 'bool', proargtypes => 'name text', prosrc => 'nameiclike' },
 { oid => '1636',
   proname => 'nameicnlike', prorettype => 'bool', proargtypes => 'name text',
   prosrc => 'nameicnlike' },
@@ -3324,20 +3337,21 @@
   prosrc => 'like_escape' },
 
 { oid => '1656',
-  proname => 'bpcharicregexeq', prorettype => 'bool',
-  proargtypes => 'bpchar text', prosrc => 'texticregexeq' },
+  proname => 'bpcharicregexeq', prosupport => 'texticregexeq_support',
+  prorettype => 'bool', proargtypes => 'bpchar text',
+  prosrc => 'texticregexeq' },
 { oid => '1657',
   proname => 'bpcharicregexne', prorettype => 'bool',
   proargtypes => 'bpchar text', prosrc => 'texticregexne' },
 { oid => '1658',
-  proname => 'bpcharregexeq', prorettype => 'bool',
-  proargtypes => 'bpchar text', prosrc => 'textregexeq' },
+  proname => 'bpcharregexeq', prosupport => 'textregexeq_support',
+  prorettype => 'bool', proargtypes => 'bpchar text', prosrc => 'textregexeq' },
 { oid => '1659',
   proname => 'bpcharregexne', prorettype => 'bool',
   proargtypes => 'bpchar text', prosrc => 'textregexne' },
 { oid => '1660',
-  proname => 'bpchariclike', prorettype => 'bool', proargtypes => 'bpchar text',
-  prosrc => 'texticlike' },
+  proname => 'bpchariclike', prosupport => 'texticlike_support',
+  prorettype => 'bool', proargtypes => 'bpchar text', prosrc => 'texticlike' },
 { oid => '1661',
   proname => 'bpcharicnlike', prorettype => 'bool',
   proargtypes => 'bpchar text', prosrc => 'texticnlike' },
@@ -3878,17 +3892,21 @@
   proname => 'network_cmp', proleakproof => 't', prorettype => 'int4',
   proargtypes => 'inet inet', prosrc => 'network_cmp' },
 { oid => '927',
-  proname => 'network_sub', prorettype => 'bool', proargtypes => 'inet inet',
-  prosrc => 'network_sub' },
+  proname => 'network_sub', prosupport => 'network_subset_support',
+  prorettype => 'bool', proargtypes => 'inet inet', prosrc => 'network_sub' },
 { oid => '928',
-  proname => 'network_subeq', prorettype => 'bool', proargtypes => 'inet inet',
-  prosrc => 'network_subeq' },
+  proname => 'network_subeq', prosupport => 'network_subset_support',
+  prorettype => 'bool', proargtypes => 'inet inet', prosrc => 'network_subeq' },
 { oid => '929',
-  proname => 'network_sup', prorettype => 'bool', proargtypes => 'inet inet',
-  prosrc => 'network_sup' },
+  proname => 'network_sup', prosupport => 'network_subset_support',
+  prorettype => 'bool', proargtypes => 'inet inet', prosrc => 'network_sup' },
 { oid => '930',
-  proname => 'network_supeq', prorettype => 'bool', proargtypes => 'inet inet',
-  prosrc => 'network_supeq' },
+  proname => 'network_supeq', prosupport => 'network_subset_support',
+  prorettype => 'bool', proargtypes => 'inet inet', prosrc => 'network_supeq' },
+{ oid => '1173', descr => 'planner support for network_sub/superset',
+  proname => 'network_subset_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'network_subset_support' },
+
 { oid => '3551',
   proname => 'network_overlap', prorettype => 'bool',
   proargtypes => 'inet inet', prosrc => 'network_overlap' },
@@ -5482,14 +5500,14 @@
   prosrc => 'select $1::pg_catalog.text || $2' },
 
 { oid => '2005',
-  proname => 'bytealike', prorettype => 'bool', proargtypes => 'bytea bytea',
-  prosrc => 'bytealike' },
+  proname => 'bytealike', prosupport => 'textlike_support',
+  prorettype => 'bool', proargtypes => 'bytea bytea', prosrc => 'bytealike' },
 { oid => '2006',
   proname => 'byteanlike', prorettype => 'bool', proargtypes => 'bytea bytea',
   prosrc => 'byteanlike' },
 { oid => '2007', descr => 'matches LIKE expression',
-  proname => 'like', prorettype => 'bool', proargtypes => 'bytea bytea',
-  prosrc => 'bytealike' },
+  proname => 'like', prosupport => 'textlike_support', prorettype => 'bool',
+  proargtypes => 'bytea bytea', prosrc => 'bytealike' },
 { oid => '2008', descr => 'does not match LIKE expression',
   proname => 'notlike', prorettype => 'bool', proargtypes => 'bytea bytea',
   prosrc => 'byteanlike' },
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 453079a..f938925 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -510,7 +510,8 @@ typedef enum NodeTag
 	T_SupportRequestSimplify,	/* in nodes/supportnodes.h */
 	T_SupportRequestSelectivity,	/* in nodes/supportnodes.h */
 	T_SupportRequestCost,		/* in nodes/supportnodes.h */
-	T_SupportRequestRows		/* in nodes/supportnodes.h */
+	T_SupportRequestRows,		/* in nodes/supportnodes.h */
+	T_SupportRequestIndexCondition	/* in nodes/supportnodes.h */
 } NodeTag;
 
 /*
diff --git a/src/include/nodes/supportnodes.h b/src/include/nodes/supportnodes.h
index 1a3a36b..5778fcb 100644
--- a/src/include/nodes/supportnodes.h
+++ b/src/include/nodes/supportnodes.h
@@ -35,7 +35,8 @@
 
 #include "nodes/primnodes.h"
 
-struct PlannerInfo;				/* avoid including relation.h here */
+struct PlannerInfo;				/* avoid including pathnodes.h here */
+struct IndexOptInfo;
 struct SpecialJoinInfo;
 
 
@@ -167,4 +168,41 @@ typedef struct SupportRequestRows
 	double		rows;			/* number of rows expected to be returned */
 } SupportRequestRows;
 
+/*
+ * The IndexCondition request allows the support function to generate
+ * a directly-indexable condition based on a target function call that is
+ * not itself indexable.  The target function call must appear at the top
+ * level of WHERE or JOIN/ON, so this applies only to functions returning
+ * boolean.
+ *
+ * The "node" argument is the parse node that is invoking the target function;
+ * currently this will always be a FuncExpr or OpExpr.  The call is made
+ * only if at least one function argument matches an index column's variable
+ * or expression.  "indexarg" identifies the matching argument (it's the
+ * zero-based position in the node's args list).
+ *
+ * If the transformation is possible, return a List of directly-indexable
+ * condition expressions, else return NULL.
+ *
+ * XXX much more to write here.
+ */
+typedef struct SupportRequestIndexCondition
+{
+	NodeTag		type;
+
+	/* Input fields: */
+	struct PlannerInfo *root;	/* Planner's infrastructure */
+	Oid			funcid;			/* function we are inquiring about */
+	Node	   *node;			/* parse node invoking function */
+	int			indexarg;		/* index of function arg matching indexcol */
+	struct IndexOptInfo *index; /* planner's info about target index */
+	int			indexcol;		/* index of target index column (0-based) */
+	Oid			opfamily;		/* index column's operator family */
+	Oid			indexcollation; /* index column's collation */
+
+	/* Output fields: */
+	bool		lossy;			/* set to false if index condition is an exact
+								 * equivalent of the function call */
+} SupportRequestIndexCondition;
+
 #endif							/* SUPPORTNODES_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..b21298a 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -105,6 +105,15 @@ SELECT b.*
 set enable_seqscan to false;
 set enable_indexscan to true;
 set enable_bitmapscan to false;
+explain (costs off)
+select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
+                                  QUERY PLAN                                  
+------------------------------------------------------------------------------
+ Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
+   Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text))
+   Filter: (proname ~~ 'RI\_FKey%del'::text)
+(3 rows)
+
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
         proname         
 ------------------------
@@ -115,8 +124,42 @@ select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
  RI_FKey_setnull_del
 (5 rows)
 
+explain (costs off)
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+                             QUERY PLAN                             
+--------------------------------------------------------------------
+ Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
+   Index Cond: ((proname >= '00'::text) AND (proname < '01'::text))
+   Filter: (proname ~~* '00%foo'::text)
+(3 rows)
+
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+ proname 
+---------
+(0 rows)
+
+explain (costs off)
+select proname from pg_proc where proname ilike 'ri%foo' order by 1;
+                           QUERY PLAN                            
+-----------------------------------------------------------------
+ Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
+   Filter: (proname ~~* 'ri%foo'::text)
+(2 rows)
+
 set enable_indexscan to false;
 set enable_bitmapscan to true;
+explain (costs off)
+select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
+                                        QUERY PLAN                                        
+------------------------------------------------------------------------------------------
+ Sort
+   Sort Key: proname
+   ->  Bitmap Heap Scan on pg_proc
+         Filter: (proname ~~ 'RI\_FKey%del'::text)
+         ->  Bitmap Index Scan on pg_proc_proname_args_nsp_index
+               Index Cond: ((proname >= 'RI_FKey'::text) AND (proname < 'RI_FKez'::text))
+(6 rows)
+
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
         proname         
 ------------------------
@@ -127,6 +170,34 @@ select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
  RI_FKey_setnull_del
 (5 rows)
 
+explain (costs off)
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Sort
+   Sort Key: proname
+   ->  Bitmap Heap Scan on pg_proc
+         Filter: (proname ~~* '00%foo'::text)
+         ->  Bitmap Index Scan on pg_proc_proname_args_nsp_index
+               Index Cond: ((proname >= '00'::text) AND (proname < '01'::text))
+(6 rows)
+
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+ proname 
+---------
+(0 rows)
+
+explain (costs off)
+select proname from pg_proc where proname ilike 'ri%foo' order by 1;
+                           QUERY PLAN                            
+-----------------------------------------------------------------
+ Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
+   Filter: (proname ~~* 'ri%foo'::text)
+(2 rows)
+
+reset enable_seqscan;
+reset enable_indexscan;
+reset enable_bitmapscan;
 --
 -- Test B-tree page deletion. In particular, deleting a non-leaf page.
 --
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 4932869..5d4eb59 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -3201,6 +3201,24 @@ explain (costs off)
          Index Cond: (b = false)
 (3 rows)
 
+explain (costs off)
+  select * from boolindex where b is true order by i desc limit 10;
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Limit
+   ->  Index Scan Backward using boolindex_b_i_key on boolindex
+         Index Cond: (b = true)
+(3 rows)
+
+explain (costs off)
+  select * from boolindex where b is false order by i desc limit 10;
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Limit
+   ->  Index Scan Backward using boolindex_b_i_key on boolindex
+         Index Cond: (b = false)
+(3 rows)
+
 --
 -- Test for multilevel page deletion
 --
diff --git a/src/test/regress/expected/inet.out b/src/test/regress/expected/inet.out
index be9427e..2420237 100644
--- a/src/test/regress/expected/inet.out
+++ b/src/test/regress/expected/inet.out
@@ -242,6 +242,15 @@ SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
 -- check that btree index works correctly
 CREATE INDEX inet_idx1 ON inet_tbl(i);
 SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ Index Scan using inet_idx1 on inet_tbl
+   Index Cond: ((i > '192.168.1.0/24'::inet) AND (i <= '192.168.1.255'::inet))
+   Filter: (i << '192.168.1.0/24'::inet)
+(3 rows)
+
 SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
        c        |        i         
 ----------------+------------------
@@ -250,6 +259,15 @@ SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
  192.168.1.0/26 | 192.168.1.226
 (3 rows)
 
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Index Scan using inet_idx1 on inet_tbl
+   Index Cond: ((i >= '192.168.1.0/24'::inet) AND (i <= '192.168.1.255'::inet))
+   Filter: (i <<= '192.168.1.0/24'::inet)
+(3 rows)
+
 SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
        c        |        i         
 ----------------+------------------
@@ -261,6 +279,43 @@ SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
  192.168.1.0/26 | 192.168.1.226
 (6 rows)
 
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i;
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ Index Scan using inet_idx1 on inet_tbl
+   Index Cond: ((i >= '192.168.1.0/24'::inet) AND (i <= '192.168.1.255'::inet))
+   Filter: ('192.168.1.0/24'::inet >>= i)
+(3 rows)
+
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/24
+ 192.168.1.0/24 | 192.168.1.226/24
+ 192.168.1.0/24 | 192.168.1.255/24
+ 192.168.1.0/24 | 192.168.1.0/25
+ 192.168.1.0/24 | 192.168.1.255/25
+ 192.168.1.0/26 | 192.168.1.226
+(6 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i;
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ Index Scan using inet_idx1 on inet_tbl
+   Index Cond: ((i > '192.168.1.0/24'::inet) AND (i <= '192.168.1.255'::inet))
+   Filter: ('192.168.1.0/24'::inet >> i)
+(3 rows)
+
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i;
+       c        |        i         
+----------------+------------------
+ 192.168.1.0/24 | 192.168.1.0/25
+ 192.168.1.0/24 | 192.168.1.255/25
+ 192.168.1.0/26 | 192.168.1.226
+(3 rows)
+
 SET enable_seqscan TO on;
 DROP INDEX inet_idx1;
 -- check that gist index works correctly
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index 054faabb..6ff2fd3 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -294,6 +294,105 @@ order by thousand, tenthous;
       999 |     9999
 (25 rows)
 
+explain (costs off)
+select thousand, tenthous, four from tenk1
+where (thousand, tenthous, four) > (998, 5000, 3)
+order by thousand, tenthous;
+                              QUERY PLAN                               
+-----------------------------------------------------------------------
+ Sort
+   Sort Key: thousand, tenthous
+   ->  Bitmap Heap Scan on tenk1
+         Filter: (ROW(thousand, tenthous, four) > ROW(998, 5000, 3))
+         ->  Bitmap Index Scan on tenk1_thous_tenthous
+               Index Cond: (ROW(thousand, tenthous) >= ROW(998, 5000))
+(6 rows)
+
+select thousand, tenthous, four from tenk1
+where (thousand, tenthous, four) > (998, 5000, 3)
+order by thousand, tenthous;
+ thousand | tenthous | four 
+----------+----------+------
+      998 |     5998 |    2
+      998 |     6998 |    2
+      998 |     7998 |    2
+      998 |     8998 |    2
+      998 |     9998 |    2
+      999 |      999 |    3
+      999 |     1999 |    3
+      999 |     2999 |    3
+      999 |     3999 |    3
+      999 |     4999 |    3
+      999 |     5999 |    3
+      999 |     6999 |    3
+      999 |     7999 |    3
+      999 |     8999 |    3
+      999 |     9999 |    3
+(15 rows)
+
+explain (costs off)
+select thousand, tenthous from tenk1
+where (998, 5000) < (thousand, tenthous)
+order by thousand, tenthous;
+                        QUERY PLAN                        
+----------------------------------------------------------
+ Index Only Scan using tenk1_thous_tenthous on tenk1
+   Index Cond: (ROW(thousand, tenthous) > ROW(998, 5000))
+(2 rows)
+
+select thousand, tenthous from tenk1
+where (998, 5000) < (thousand, tenthous)
+order by thousand, tenthous;
+ thousand | tenthous 
+----------+----------
+      998 |     5998
+      998 |     6998
+      998 |     7998
+      998 |     8998
+      998 |     9998
+      999 |      999
+      999 |     1999
+      999 |     2999
+      999 |     3999
+      999 |     4999
+      999 |     5999
+      999 |     6999
+      999 |     7999
+      999 |     8999
+      999 |     9999
+(15 rows)
+
+explain (costs off)
+select thousand, hundred from tenk1
+where (998, 5000) < (thousand, hundred)
+order by thousand, hundred;
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Sort
+   Sort Key: thousand, hundred
+   ->  Bitmap Heap Scan on tenk1
+         Filter: (ROW(998, 5000) < ROW(thousand, hundred))
+         ->  Bitmap Index Scan on tenk1_thous_tenthous
+               Index Cond: (thousand >= 998)
+(6 rows)
+
+select thousand, hundred from tenk1
+where (998, 5000) < (thousand, hundred)
+order by thousand, hundred;
+ thousand | hundred 
+----------+---------
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+      999 |      99
+(10 rows)
+
 -- Test case for bug #14010: indexed row comparisons fail with nulls
 create temp table test_table (a text, b text);
 insert into test_table values ('a', 'b');
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..2b087be 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -59,11 +59,29 @@ SELECT b.*
 set enable_seqscan to false;
 set enable_indexscan to true;
 set enable_bitmapscan to false;
+explain (costs off)
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
+select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
+explain (costs off)
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+explain (costs off)
+select proname from pg_proc where proname ilike 'ri%foo' order by 1;
 
 set enable_indexscan to false;
 set enable_bitmapscan to true;
+explain (costs off)
+select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
 select proname from pg_proc where proname like E'RI\\_FKey%del' order by 1;
+explain (costs off)
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+select proname from pg_proc where proname ilike '00%foo' order by 1;
+explain (costs off)
+select proname from pg_proc where proname ilike 'ri%foo' order by 1;
+
+reset enable_seqscan;
+reset enable_indexscan;
+reset enable_bitmapscan;
 
 --
 -- Test B-tree page deletion. In particular, deleting a non-leaf page.
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 59da6b6..67ecad8 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -1135,6 +1135,10 @@ explain (costs off)
   select * from boolindex where b = true order by i desc limit 10;
 explain (costs off)
   select * from boolindex where not b order by i limit 10;
+explain (costs off)
+  select * from boolindex where b is true order by i desc limit 10;
+explain (costs off)
+  select * from boolindex where b is false order by i desc limit 10;
 
 --
 -- Test for multilevel page deletion
diff --git a/src/test/regress/sql/inet.sql b/src/test/regress/sql/inet.sql
index 880e115..bbfa9d3 100644
--- a/src/test/regress/sql/inet.sql
+++ b/src/test/regress/sql/inet.sql
@@ -65,8 +65,18 @@ SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
 -- check that btree index works correctly
 CREATE INDEX inet_idx1 ON inet_tbl(i);
 SET enable_seqscan TO off;
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
 SELECT * FROM inet_tbl WHERE i<<'192.168.1.0/24'::cidr;
+EXPLAIN (COSTS OFF)
 SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
+SELECT * FROM inet_tbl WHERE i<<='192.168.1.0/24'::cidr;
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i;
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >>= i;
+EXPLAIN (COSTS OFF)
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i;
+SELECT * FROM inet_tbl WHERE '192.168.1.0/24'::cidr >> i;
 SET enable_seqscan TO on;
 DROP INDEX inet_idx1;
 
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 454d462..ea93347 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -119,6 +119,33 @@ select thousand, tenthous from tenk1
 where (thousand, tenthous) >= (997, 5000)
 order by thousand, tenthous;
 
+explain (costs off)
+select thousand, tenthous, four from tenk1
+where (thousand, tenthous, four) > (998, 5000, 3)
+order by thousand, tenthous;
+
+select thousand, tenthous, four from tenk1
+where (thousand, tenthous, four) > (998, 5000, 3)
+order by thousand, tenthous;
+
+explain (costs off)
+select thousand, tenthous from tenk1
+where (998, 5000) < (thousand, tenthous)
+order by thousand, tenthous;
+
+select thousand, tenthous from tenk1
+where (998, 5000) < (thousand, tenthous)
+order by thousand, tenthous;
+
+explain (costs off)
+select thousand, hundred from tenk1
+where (998, 5000) < (thousand, hundred)
+order by thousand, hundred;
+
+select thousand, hundred from tenk1
+where (998, 5000) < (thousand, hundred)
+order by thousand, hundred;
+
 -- Test case for bug #14010: indexed row comparisons fail with nulls
 create temp table test_table (a text, b text);
 insert into test_table values ('a', 'b');
