diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ca17faf8a5f71b14fe4571059c4a6d5996bf6e5f..db97c2ed39bb57ef47f85492ca96d625286f55fa 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.88 1999/07/25 17:53:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.89 1999/07/27 03:51:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1097,7 +1097,6 @@ CopyPathFields(Path *from, Path *newnode)
 	newnode->outerjoincost = from->outerjoincost;
 
 	newnode->joinid = listCopy(from->joinid);
-	Node_Copy(from, newnode, loc_restrictinfo);
 }
 
 /* ----------------
diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c
index 8bedf17fdb971187f065518a78aab0d566521eff..b06536d19777c04f78f18995a8bf8a62dc23b9ce 100644
--- a/src/backend/nodes/freefuncs.c
+++ b/src/backend/nodes/freefuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.23 1999/07/25 17:53:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.24 1999/07/27 03:51:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -750,7 +750,6 @@ FreePathFields(Path *node)
 	freeObject(node->pathkeys);
 
 	freeList(node->joinid);
-	freeObject(node->loc_restrictinfo);
 }
 
 /* ----------------
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2f1002c78f5b5e1eb31045e386f877b110869b84..670a41c60adcbcf39d7e227fe7497f03af07d6a3 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -8,10 +8,11 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.65 1999/07/25 23:07:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.66 1999/07/27 03:51:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
+#include <ctype.h>
 #include <math.h>
 
 #include "postgres.h"
@@ -20,6 +21,7 @@
 #include "access/nbtree.h"
 #include "catalog/catname.h"
 #include "catalog/pg_amop.h"
+#include "catalog/pg_operator.h"
 #include "executor/executor.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -35,8 +37,13 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
 #include "parser/parsetree.h"
+#include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/syscache.h"
 
+typedef enum {
+	Prefix_None, Prefix_Partial, Prefix_Exact
+} Prefix_Status;
 
 static void match_index_orclauses(RelOptInfo *rel, RelOptInfo *index, int indexkey,
 					  int xclass, List *restrictinfo_list);
@@ -65,6 +72,12 @@ static List *create_index_path_group(Query *root, RelOptInfo *rel, RelOptInfo *i
 static bool match_index_to_operand(int indexkey, Expr *operand,
 								   RelOptInfo *rel, RelOptInfo *index);
 static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
+static bool match_special_index_operator(Expr *clause, bool indexkey_on_left);
+static Prefix_Status like_fixed_prefix(char *patt, char **prefix);
+static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive,
+										char **prefix);
+static List *prefix_quals(Var *leftop, Oid expr_op,
+						  char *prefix, Prefix_Status pstatus);
 
 
 /*
@@ -502,7 +515,8 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
  *		  or (var op var) for a join clause, where the var or one
  *		  of the vars matches the index key; and
  *	  (2) contain an operator which is in the same class as the index
- *		  operator for this key.
+ *		  operator for this key, or is a "special" operator as recognized
+ *		  by match_special_index_operator().
  *
  *	  In the restriction case, we can cope with (const op var) by commuting
  *	  the clause to (var op const), if there is a commutator operator.
@@ -539,6 +553,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
 	bool		isIndexable = false;
 	Var		   *leftop,
 			   *rightop;
+	Oid			expr_op;
 
 	if (! is_opclause((Node *) clause))
 		return false;
@@ -546,6 +561,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
 	rightop = get_rightop(clause);
 	if (! leftop || ! rightop)
 		return false;
+	expr_op = ((Oper *) clause->oper)->opno;
 
 	if (!join)
 	{
@@ -553,23 +569,17 @@ match_clause_to_indexkey(RelOptInfo *rel,
 		 * Not considering joins, so check for clauses of the form:
 		 * (var/func operator constant) and (constant operator var/func)
 		 */
-		Oid			restrict_op = InvalidOid;
 
 		/*
 		 * Check for standard s-argable clause
 		 */
-		if (IsA(rightop, Const) || IsA(rightop, Param))
+		if ((IsA(rightop, Const) || IsA(rightop, Param)) &&
+			match_index_to_operand(indexkey, (Expr *) leftop,
+								   rel, index))
 		{
-			restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno;
-
-			isIndexable = (op_class(restrict_op, xclass, index->relam) &&
-						   match_index_to_operand(indexkey,
-												  (Expr *) leftop,
-												  rel,
-												  index));
+			isIndexable = op_class(expr_op, xclass, index->relam);
 
 #ifndef IGNORE_BINARY_COMPATIBLE_INDICES
-
 			/*
 			 * Didn't find an index? Then maybe we can find another
 			 * binary-compatible index instead... thomas 1998-08-14
@@ -583,88 +593,70 @@ match_clause_to_indexkey(RelOptInfo *rel,
 				 * make sure we have two different binary-compatible
 				 * types...
 				 */
-				if ((ltype != rtype)
-					&& IS_BINARY_COMPATIBLE(ltype, rtype))
+				if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
 				{
-					char	   *opname;
-					Operator	newop;
+					char	   *opname = get_opname(expr_op);
+					Operator	newop = NULL;
 
-					opname = get_opname(restrict_op);
 					if (opname != NULL)
 						newop = oper(opname, ltype, ltype, TRUE);
-					else
-						newop = NULL;
 
 					/* actually have a different operator to try? */
-					if (HeapTupleIsValid(newop) &&
-						(oprid(newop) != restrict_op))
+					if (HeapTupleIsValid(newop) && oprid(newop) != expr_op)
 					{
-						restrict_op = oprid(newop);
-
-						isIndexable = (op_class(restrict_op, xclass, index->relam) &&
-									   match_index_to_operand(indexkey,
-															  (Expr *) leftop,
-															  rel,
-															  index));
-
+						expr_op = oprid(newop);
+						isIndexable = op_class(expr_op, xclass, index->relam);
 						if (isIndexable)
-							((Oper *) ((Expr *) clause)->oper)->opno = restrict_op;
+							((Oper *) clause->oper)->opno = expr_op;
 					}
 				}
 			}
 #endif
+
+			/*
+			 * If we didn't find a member of the index's opclass,
+			 * see whether it is a "special" indexable operator.
+			 */
+			if (!isIndexable)
+				isIndexable = match_special_index_operator(clause, true);
+
 		}
 
 		/*
 		 * Must try to commute the clause to standard s-arg format.
+		 * XXX do we really have to commute it?  The executor doesn't care!
 		 */
-		else if (IsA(leftop, Const) || IsA(leftop, Param))
+		else if ((IsA(leftop, Const) || IsA(leftop, Param)) &&
+				 match_index_to_operand(indexkey, (Expr *) rightop,
+										rel, index))
 		{
-			restrict_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);
+			Oid		commuted_op = get_commutator(expr_op);
 
-			isIndexable = ((restrict_op != InvalidOid) &&
-						   op_class(restrict_op, xclass, index->relam) &&
-						   match_index_to_operand(indexkey,
-												  (Expr *) rightop,
-												  rel,
-												  index));
+			isIndexable = ((commuted_op != InvalidOid) &&
+						   op_class(commuted_op, xclass, index->relam));
 
 #ifndef IGNORE_BINARY_COMPATIBLE_INDICES
 			if (!isIndexable)
 			{
-				Oid			ltype;
-				Oid			rtype;
-
-				ltype = exprType((Node *) leftop);
-				rtype = exprType((Node *) rightop);
+				Oid			ltype = exprType((Node *) leftop);
+				Oid			rtype = exprType((Node *) rightop);
 
-				if ((ltype != rtype)
-					&& IS_BINARY_COMPATIBLE(ltype, rtype))
+				if (ltype != rtype && IS_BINARY_COMPATIBLE(ltype, rtype))
 				{
-					char	   *opname;
-					Operator	newop;
+					char	   *opname = get_opname(expr_op);
+					Operator	newop = NULL;
 
-					restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno;
-
-					opname = get_opname(restrict_op);
+					/* note we use rtype, ie, the indexkey's type */
 					if (opname != NULL)
 						newop = oper(opname, rtype, rtype, TRUE);
-					else
-						newop = NULL;
 
-					if (HeapTupleIsValid(newop) && (oprid(newop) != restrict_op))
+					if (HeapTupleIsValid(newop) && oprid(newop) != expr_op)
 					{
-						restrict_op = get_commutator(oprid(newop));
-
-						isIndexable = ((restrict_op != InvalidOid) &&
-						   op_class(restrict_op, xclass, index->relam) &&
-									   match_index_to_operand(indexkey,
-															  (Expr *) rightop,
-															  rel,
-															  index));
-
+						expr_op = get_commutator(oprid(newop));
+						isIndexable = (expr_op != InvalidOid) &&
+							op_class(expr_op, xclass, index->relam);
 						if (isIndexable)
-							((Oper *) ((Expr *) clause)->oper)->opno = oprid(newop);
+							((Oper *) clause->oper)->opno = oprid(newop);
 					}
 				}
 			}
@@ -672,13 +664,22 @@ match_clause_to_indexkey(RelOptInfo *rel,
 
 			if (isIndexable)
 			{
-
 				/*
 				 * In place list modification. (op const var/func) -> (op
 				 * var/func const)
 				 */
 				CommuteClause((Node *) clause);
 			}
+			else
+			{
+				/*
+				 * If we didn't find a member of the index's opclass,
+				 * see whether it is a "special" indexable operator.
+				 * (match_special_index_operator must commute the
+				 * clause itself, if it wants to.)
+				 */
+				isIndexable = match_special_index_operator(clause, false);
+			}
 		}
 	}
 	else
@@ -694,10 +695,10 @@ match_clause_to_indexkey(RelOptInfo *rel,
 
 		if (match_index_to_operand(indexkey, (Expr *) leftop,
 								   rel, index))
-			join_op = ((Oper *) ((Expr *) clause)->oper)->opno;
+			join_op = expr_op;
 		else if (match_index_to_operand(indexkey, (Expr *) rightop,
 										rel, index))
-			join_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno);
+			join_op = get_commutator(expr_op);
 
 		if (join_op && op_class(join_op, xclass, index->relam) &&
 			is_joinable((Node *) clause))
@@ -1221,6 +1222,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
 		float		selec;
 
 		indexquals = get_actual_clauses(clausegroup);
+		/* expand special operators to indexquals the executor can handle */
+		indexquals = expand_indexqual_conditions(indexquals);
 
 		index_selectivity(root,
 						  lfirsti(rel->relids),
@@ -1258,18 +1261,6 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
 											  index->tuples,
 											  true);
 
-		/*
-		 * copy restrictinfo list into path for expensive function
-		 * processing -- JMH, 7/7/92
-		 */
-		pathnode->path.loc_restrictinfo = set_difference(copyObject((Node *) rel->restrictinfo),
-														 clausegroup);
-
-#ifdef NOT_USED					/* fix xfunc */
-		/* add in cost for expensive functions!  -- JMH, 7/7/92 */
-		if (XfuncMode != XFUNC_OFF)
-			((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path *) pathnode);
-#endif
 		path_list = lappend(path_list, pathnode);
 		outerrelids_list = lnext(outerrelids_list);
 	}
@@ -1419,3 +1410,460 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
 
 	return true;
 }
+
+/****************************************************************************
+ *			----  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.)
+ *
+ * Two routines are provided here, match_special_index_operator() and
+ * expand_indexqual_conditions().  match_special_index_operator() is
+ * just an auxiliary function for match_clause_to_indexkey(); after
+ * the latter fails to recognize a restriction opclause's operator
+ * as a member of an index's opclass, it asks match_special_index_operator()
+ * whether the clause should be considered an indexqual anyway.
+ * expand_indexqual_conditions() converts a list of "raw" indexqual
+ * conditions (with implicit AND semantics across list elements) into
+ * a list that the executor can actually handle.  For operators that
+ * are members of the index's opclass this transformation is a no-op,
+ * but operators recognized by match_special_index_operator() must be
+ * converted into one or more "regular" indexqual conditions.
+ *----------
+ */
+
+/*
+ * 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 const/param) or (const/param OP indexkey),
+ * but the OP proved not to be one of the index's opclass operators.
+ * Return 'true' if we can do something with it anyway.
+ */
+static bool
+match_special_index_operator(Expr *clause, bool indexkey_on_left)
+{
+	bool		isIndexable = false;
+	Var		   *leftop,
+			   *rightop;
+	Oid			expr_op;
+	Datum		constvalue;
+	char	   *patt;
+	char	   *prefix;
+
+	/* 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 */
+	leftop = get_leftop(clause);
+	rightop = get_rightop(clause);
+	expr_op = ((Oper *) clause->oper)->opno;
+
+	/* again, required for all current special ops: */
+	if (! IsA(rightop, Const) ||
+		((Const *) rightop)->constisnull)
+		return false;
+	constvalue = ((Const *) rightop)->constvalue;
+
+	switch (expr_op)
+	{
+		case OID_TEXT_LIKE_OP:
+		case OID_BPCHAR_LIKE_OP:
+		case OID_VARCHAR_LIKE_OP:
+		case OID_NAME_LIKE_OP:
+			/* the right-hand const is type text for all of these */
+			patt = textout((text *) DatumGetPointer(constvalue));
+			isIndexable = like_fixed_prefix(patt, &prefix) != Prefix_None;
+			if (prefix) pfree(prefix);
+			pfree(patt);
+			break;
+
+		case OID_TEXT_REGEXEQ_OP:
+		case OID_BPCHAR_REGEXEQ_OP:
+		case OID_VARCHAR_REGEXEQ_OP:
+		case OID_NAME_REGEXEQ_OP:
+			/* the right-hand const is type text for all of these */
+			patt = textout((text *) DatumGetPointer(constvalue));
+			isIndexable = regex_fixed_prefix(patt, false, &prefix) != Prefix_None;
+			if (prefix) pfree(prefix);
+			pfree(patt);
+			break;
+
+		case OID_TEXT_ICREGEXEQ_OP:
+		case OID_BPCHAR_ICREGEXEQ_OP:
+		case OID_VARCHAR_ICREGEXEQ_OP:
+		case OID_NAME_ICREGEXEQ_OP:
+			/* the right-hand const is type text for all of these */
+			patt = textout((text *) DatumGetPointer(constvalue));
+			isIndexable = regex_fixed_prefix(patt, true, &prefix) != Prefix_None;
+			if (prefix) pfree(prefix);
+			pfree(patt);
+			break;
+	}
+
+	return isIndexable;
+}
+
+/*
+ * expand_indexqual_conditions
+ *	  Given a list of (implicitly ANDed) indexqual clauses,
+ *	  expand any "special" index operators into clauses that the indexscan
+ *	  machinery will know what to do with.  Clauses that were not
+ *	  recognized by match_special_index_operator() must be passed through
+ *	  unchanged.
+ */
+List *
+expand_indexqual_conditions(List *indexquals)
+{
+	List	   *resultquals = NIL;
+	List	   *q;
+
+	foreach(q, indexquals)
+	{
+		Expr	   *clause = (Expr *) lfirst(q);
+		/* we know these will succeed */
+		Var		   *leftop = get_leftop(clause);
+		Var		   *rightop = get_rightop(clause);
+		Oid			expr_op = ((Oper *) clause->oper)->opno;
+		Datum		constvalue;
+		char	   *patt;
+		char	   *prefix;
+		Prefix_Status pstatus;
+
+		switch (expr_op)
+		{
+			/*
+			 * LIKE and regex operators are not members of any index opclass,
+			 * so if we find one in an indexqual list we can assume that
+			 * it was accepted by match_special_index_operator().
+			 */
+			case OID_TEXT_LIKE_OP:
+			case OID_BPCHAR_LIKE_OP:
+			case OID_VARCHAR_LIKE_OP:
+			case OID_NAME_LIKE_OP:
+				/* the right-hand const is type text for all of these */
+				constvalue = ((Const *) rightop)->constvalue;
+				patt = textout((text *) DatumGetPointer(constvalue));
+				pstatus = like_fixed_prefix(patt, &prefix);
+				resultquals = nconc(resultquals,
+									prefix_quals(leftop, expr_op,
+												 prefix, pstatus));
+				if (prefix) pfree(prefix);
+				pfree(patt);
+				break;
+
+			case OID_TEXT_REGEXEQ_OP:
+			case OID_BPCHAR_REGEXEQ_OP:
+			case OID_VARCHAR_REGEXEQ_OP:
+			case OID_NAME_REGEXEQ_OP:
+				/* the right-hand const is type text for all of these */
+				constvalue = ((Const *) rightop)->constvalue;
+				patt = textout((text *) DatumGetPointer(constvalue));
+				pstatus = regex_fixed_prefix(patt, false, &prefix);
+				resultquals = nconc(resultquals,
+									prefix_quals(leftop, expr_op,
+												 prefix, pstatus));
+				if (prefix) pfree(prefix);
+				pfree(patt);
+				break;
+
+			case OID_TEXT_ICREGEXEQ_OP:
+			case OID_BPCHAR_ICREGEXEQ_OP:
+			case OID_VARCHAR_ICREGEXEQ_OP:
+			case OID_NAME_ICREGEXEQ_OP:
+				/* the right-hand const is type text for all of these */
+				constvalue = ((Const *) rightop)->constvalue;
+				patt = textout((text *) DatumGetPointer(constvalue));
+				pstatus = regex_fixed_prefix(patt, true, &prefix);
+				resultquals = nconc(resultquals,
+									prefix_quals(leftop, expr_op,
+												 prefix, pstatus));
+				if (prefix) pfree(prefix);
+				pfree(patt);
+				break;
+
+			default:
+				resultquals = lappend(resultquals, clause);
+				break;
+		}
+	}
+
+	return resultquals;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a LIKE pattern.
+ * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+like_fixed_prefix(char *patt, char **prefix)
+{
+	char	   *match;
+	int			pos,
+				match_pos;
+
+	*prefix = match = palloc(strlen(patt)+2);
+	match_pos = 0;
+
+	for (pos = 0; patt[pos]; pos++)
+	{
+		/* % and _ are wildcard characters in LIKE */
+		if (patt[pos] == '%' ||
+			patt[pos] == '_')
+			break;
+		/* Backslash quotes the next character */
+		if (patt[pos] == '\\')
+		{
+			pos++;
+			if (patt[pos] == '\0')
+				break;
+		}
+		/*
+		 * NOTE: this code used to think that %% meant a literal %,
+		 * but textlike() itself does not think that, and the SQL92
+		 * spec doesn't say any such thing either.
+		 */
+		match[match_pos++] = patt[pos];
+	}
+	
+	match[match_pos] = '\0';
+
+	/* in LIKE, an empty pattern is an exact match! */
+	if (patt[pos] == '\0')
+		return Prefix_Exact;	/* reached end of pattern, so exact */
+
+	if (match_pos > 0)
+		return Prefix_Partial;
+	return Prefix_None;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a regex pattern.
+ * *prefix is set to a palloc'd prefix string with 1 spare byte,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+regex_fixed_prefix(char *patt, bool case_insensitive,
+				   char **prefix)
+{
+	char	   *match;
+	int			pos,
+				match_pos;
+
+	*prefix = NULL;
+
+	/* Pattern must be anchored left */
+	if (patt[0] != '^')
+		return Prefix_None;
+
+	/* Cannot optimize if unquoted | { } is present in pattern */
+	for (pos = 1; patt[pos]; pos++)
+	{
+		if (patt[pos] == '|' ||
+			patt[pos] == '{' ||
+			patt[pos] == '}')
+			return Prefix_None;
+		if (patt[pos] == '\\')
+		{
+			pos++;
+			if (patt[pos] == '\0')
+				break;
+		}
+	}
+
+	/* OK, allocate space for pattern */
+	*prefix = match = palloc(strlen(patt)+2);
+	match_pos = 0;
+
+	/* note start at pos 1 to skip leading ^ */
+	for (pos = 1; patt[pos]; pos++)
+	{
+		if (patt[pos] == '.' ||
+			patt[pos] == '?' ||
+			patt[pos] == '*' ||
+			patt[pos] == '[' ||
+			patt[pos] == '$' ||
+			/* XXX I suspect isalpha() is not an adequately locale-sensitive
+			 * test for characters that can vary under case folding?
+			 */
+			(case_insensitive && isalpha(patt[pos])))
+			break;
+		if (patt[pos] == '\\')
+		{
+			pos++;
+			if (patt[pos] == '\0')
+				break;
+		}
+		match[match_pos++] = patt[pos];
+	}
+
+	match[match_pos] = '\0';
+
+	if (patt[pos] == '$' && patt[pos+1] == '\0')
+		return Prefix_Exact;	/* pattern specifies exact match */
+	
+	if (match_pos > 0)
+		return Prefix_Partial;
+	return Prefix_None;
+}
+
+/*
+ * Given a fixed prefix that all the "leftop" values must have,
+ * generate suitable indexqual condition(s).  expr_op is the original
+ * LIKE or regex operator; we use it to deduce the appropriate comparison
+ * operators.
+ */
+static List *
+prefix_quals(Var *leftop, Oid expr_op,
+			 char *prefix, Prefix_Status pstatus)
+{
+	List	   *result;
+	Oid			datatype;
+	HeapTuple	optup;
+	void	   *conval;
+	Const	   *con;
+	Oper	   *op;
+	Expr	   *expr;
+	int			prefixlen;
+
+	Assert(pstatus != Prefix_None);
+
+	switch (expr_op)
+	{
+		case OID_TEXT_LIKE_OP:
+		case OID_TEXT_REGEXEQ_OP:
+		case OID_TEXT_ICREGEXEQ_OP:
+			datatype = TEXTOID;
+			break;
+
+		case OID_BPCHAR_LIKE_OP:
+		case OID_BPCHAR_REGEXEQ_OP:
+		case OID_BPCHAR_ICREGEXEQ_OP:
+			datatype = BPCHAROID;
+			break;
+
+		case OID_VARCHAR_LIKE_OP:
+		case OID_VARCHAR_REGEXEQ_OP:
+		case OID_VARCHAR_ICREGEXEQ_OP:
+			datatype = VARCHAROID;
+			break;
+
+		case OID_NAME_LIKE_OP:
+		case OID_NAME_REGEXEQ_OP:
+		case OID_NAME_ICREGEXEQ_OP:
+			datatype = NAMEOID;
+			break;
+
+		default:
+			elog(ERROR, "prefix_quals: unexpected operator %u", expr_op);
+			return NIL;
+	}
+
+	/*
+	 * If we found an exact-match pattern, generate an "=" indexqual.
+	 */
+	if (pstatus == Prefix_Exact)
+	{
+		optup = SearchSysCacheTuple(OPRNAME,
+									PointerGetDatum("="),
+									ObjectIdGetDatum(datatype),
+									ObjectIdGetDatum(datatype),
+									CharGetDatum('b'));
+		if (!HeapTupleIsValid(optup))
+			elog(ERROR, "prefix_quals: no = operator for type %u", datatype);
+		/* Note: we cheat a little by assuming that textin() will do for
+		 * bpchar and varchar constants too...
+		 */
+		conval = (datatype == NAMEOID) ?
+			(void*) namein(prefix) : (void*) textin(prefix);
+		con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+						PointerGetDatum(conval),
+						false, false, false, false);
+		op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+		expr = make_opclause(op, leftop, (Var *) con);
+		result = lcons(expr, NIL);
+		return result;
+	}
+
+	/*
+	 * Otherwise, we have a nonempty required prefix of the values.
+	 *
+	 * We can always say "x >= prefix".
+	 */
+	optup = SearchSysCacheTuple(OPRNAME,
+								PointerGetDatum(">="),
+								ObjectIdGetDatum(datatype),
+								ObjectIdGetDatum(datatype),
+								CharGetDatum('b'));
+	if (!HeapTupleIsValid(optup))
+		elog(ERROR, "prefix_quals: no >= operator for type %u", datatype);
+	conval = (datatype == NAMEOID) ?
+		(void*) namein(prefix) : (void*) textin(prefix);
+	con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+					PointerGetDatum(conval),
+					false, false, false, false);
+	op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+	expr = make_opclause(op, leftop, (Var *) con);
+	result = lcons(expr, NIL);
+
+	/*
+	 * In ASCII locale we say "x <= prefix\377".  This does not
+	 * work for non-ASCII collation orders, and it's not really
+	 * right even for ASCII.  FIX ME!
+	 * Note we assume the passed prefix string is workspace with
+	 * an extra byte, as created by the xxx_fixed_prefix routines above.
+	 */
+#ifndef USE_LOCALE
+	prefixlen = strlen(prefix);
+	prefix[prefixlen] = '\377';
+	prefix[prefixlen+1] = '\0';
+
+	optup = SearchSysCacheTuple(OPRNAME,
+								PointerGetDatum("<="),
+								ObjectIdGetDatum(datatype),
+								ObjectIdGetDatum(datatype),
+								CharGetDatum('b'));
+	if (!HeapTupleIsValid(optup))
+		elog(ERROR, "prefix_quals: no <= operator for type %u", datatype);
+	conval = (datatype == NAMEOID) ?
+		(void*) namein(prefix) : (void*) textin(prefix);
+	con = makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+					PointerGetDatum(conval),
+					false, false, false, false);
+	op = makeOper(optup->t_data->t_oid, InvalidOid, BOOLOID, 0, NULL);
+	expr = make_opclause(op, leftop, (Var *) con);
+	result = lappend(result, expr);
+#endif
+
+	return result;
+}
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index 39fa69cdef818940258d555bd42a8aa528306b43..450b8d7b2dc53c847036bccd7e58c14fe7517a86 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.30 1999/07/25 23:07:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,11 +28,13 @@
 
 static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
 									  List *subclauses, List *indices,
+									  List **indexquals,
 									  List **indexids,
 									  Cost *cost, Cost *selec);
 static void best_or_subclause_index(Query *root, RelOptInfo *rel,
-									Expr *subclause, List *indices,
-									int *indexid, Cost *cost, Cost *selec);
+									List *indexqual, List *indices,
+									int *retIndexid,
+									Cost *retCost, Cost *retSelec);
 
 
 /*
@@ -84,8 +86,8 @@ create_or_index_paths(Query *root,
 				 * best available index for each subclause.
 				 */
 				IndexPath  *pathnode = makeNode(IndexPath);
+				List	   *indexquals;
 				List	   *indexids;
-				List	   *orclause;
 				Cost		cost;
 				Cost		selec;
 
@@ -93,6 +95,7 @@ create_or_index_paths(Query *root,
 										  rel,
 										  clausenode->clause->args,
 										  clausenode->indexids,
+										  &indexquals,
 										  &indexids,
 										  &cost,
 										  &selec);
@@ -111,43 +114,11 @@ create_or_index_paths(Query *root,
 				pathnode->path.pathorder->ord.sortop = NULL;
 				pathnode->path.pathkeys = NIL;
 
-				/*
-				 * Generate an indexqual list from the OR clause's args.
-				 * We want two levels of sublist: the first is implicit OR
-				 * and the second is implicit AND.  (Currently, we will never
-				 * see a sub-AND-clause because of cnfify(), but someday maybe
-				 * the code below will do something useful...)
-				 */
-				pathnode->indexqual = NIL;
-				foreach(orclause, clausenode->clause->args)
-				{
-					Expr   *subclause = (Expr *) lfirst(orclause);
-					List   *sublist;
-
-					if (and_clause((Node *) subclause))
-						sublist = subclause->args;
-					else
-						sublist = lcons(subclause, NIL);
-					/* expansion call... */
-					pathnode->indexqual = lappend(pathnode->indexqual,
-												  sublist);
-				}
+				pathnode->indexqual = indexquals;
 				pathnode->indexid = indexids;
 				pathnode->path.path_cost = cost;
 				clausenode->selectivity = (Cost) selec;
 
-				/*
-				 * copy restrictinfo list into path for expensive function
-				 * processing	 -- JMH, 7/7/92
-				 */
-				pathnode->path.loc_restrictinfo = set_difference(copyObject((Node *) rel->restrictinfo),
-												 lcons(clausenode, NIL));
-
-#ifdef NOT_USED					/* fix xfunc */
-				/* add in cost for expensive functions!  -- JMH, 7/7/92 */
-				if (XfuncMode != XFUNC_OFF)
-					((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode);
-#endif
 				path_list = lappend(path_list, pathnode);
 			}
 		}
@@ -163,11 +134,21 @@ create_or_index_paths(Query *root,
  *	  indices.	The cost is the sum of the individual index costs, since
  *	  the executor will perform a scan for each subclause of the 'or'.
  *
+ * This routine also creates the indexquals and indexids lists that will
+ * be needed by the executor.  The indexquals list has one entry for each
+ * scan of the base rel, which is a sublist of indexqual conditions to
+ * apply in that scan.  The implicit semantics are AND across each sublist
+ * of quals, and OR across the toplevel list (note that the executor
+ * takes care not to return any single tuple more than once).  The indexids
+ * list gives the index to be used in each scan.
+ *
  * 'rel' is the node of the relation on which the indexes are defined
  * 'subclauses' are the subclauses of the 'or' clause
  * 'indices' is a list of sublists of the index nodes that matched each
  *		subclause of the 'or' clause
- * '*indexids' gets a list of the best index ID to use for each subclause
+ * '*indexquals' gets the constructed indexquals for the path (a list
+ *		of sublists of clauses, one sublist per scan of the base rel)
+ * '*indexids' gets a list of the index IDs for each scan of the rel
  * '*cost' gets the total cost of the path
  * '*selec' gets the total selectivity of the path.
  */
@@ -176,27 +157,41 @@ best_or_subclause_indices(Query *root,
 						  RelOptInfo *rel,
 						  List *subclauses,
 						  List *indices,
+						  List **indexquals,	/* return value */
 						  List **indexids,		/* return value */
 						  Cost *cost,			/* return value */
 						  Cost *selec)			/* return value */
 {
 	List	   *slist;
 
+	*indexquals = NIL;
 	*indexids = NIL;
-	*selec = (Cost) 0.0;
 	*cost = (Cost) 0.0;
+	*selec = (Cost) 0.0;
 
 	foreach(slist, subclauses)
 	{
+		Expr	   *subclause = lfirst(slist);
+		List	   *indexqual;
 		int			best_indexid;
 		Cost		best_cost;
 		Cost		best_selec;
 
-		best_or_subclause_index(root, rel, lfirst(slist), lfirst(indices),
+		/* Convert this 'or' subclause to an indexqual list */
+		indexqual = make_ands_implicit(subclause);
+		/* expand special operators to indexquals the executor can handle */
+		indexqual = expand_indexqual_conditions(indexqual);
+
+		best_or_subclause_index(root, rel, indexqual, lfirst(indices),
 								&best_indexid, &best_cost, &best_selec);
 
+		*indexquals = lappend(*indexquals, indexqual);
 		*indexids = lappendi(*indexids, best_indexid);
 		*cost += best_cost;
+		/* We approximate the selectivity as the sum of the clause
+		 * selectivities (but not more than 1).
+		 * XXX This is too pessimistic, isn't it?
+		 */
 		*selec += best_selec;
 		if (*selec > (Cost) 1.0)
 			*selec = (Cost) 1.0;
@@ -212,7 +207,7 @@ best_or_subclause_indices(Query *root,
  *	  the least expensive.
  *
  * 'rel' is the node of the relation on which the index is defined
- * 'subclause' is the subclause
+ * 'indexqual' is the indexqual list derived from the subclause
  * 'indices' is a list of index nodes that match the subclause
  * '*retIndexid' gets the ID of the best index
  * '*retCost' gets the cost of a scan with that index
@@ -221,14 +216,13 @@ best_or_subclause_indices(Query *root,
 static void
 best_or_subclause_index(Query *root,
 						RelOptInfo *rel,
-						Expr *subclause,
+						List *indexqual,
 						List *indices,
 						int *retIndexid,		/* return value */
 						Cost *retCost,	/* return value */
 						Cost *retSelec) /* return value */
 {
 	bool		first_run = true;
-	List	   *indexquals;
 	List	   *ilist;
 
 	/* if we don't match anything, return zeros */
@@ -236,13 +230,6 @@ best_or_subclause_index(Query *root,
 	*retCost = (Cost) 0.0;
 	*retSelec = (Cost) 0.0;
 
-	/* convert 'or' subclause to an indexqual list */
-	if (and_clause((Node *) subclause))
-		indexquals = subclause->args;
-	else
-		indexquals = lcons(subclause, NIL);
-	/* expansion call... */
-
 	foreach(ilist, indices)
 	{
 		RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
@@ -254,7 +241,7 @@ best_or_subclause_index(Query *root,
 		index_selectivity(root,
 						  lfirsti(rel->relids),
 						  indexid,
-						  indexquals,
+						  indexqual,
 						  &npages,
 						  &selec);
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 0148d4912e198feca8c57c9dfa6470d42f3111ee..18837cf2824095c1fd29a6800648c1677f5fc5ac 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.64 1999/07/27 03:51:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -151,15 +151,11 @@ create_scan_node(Path *best_path, List *tlist)
 	List	   *scan_clauses;
 
 	/*
-	 * Extract the relevant clauses from the parent relation and replace
-	 * the operator OIDs with the corresponding regproc ids.
-	 *
-	 * now that local predicate clauses are copied into paths in
-	 * find_rel_paths() and then (possibly) pulled up in
-	 * xfunc_trypullup(), we get the relevant clauses from the path
-	 * itself, not its parent relation.   --- JMH, 6/15/92
+	 * Extract the relevant restriction clauses from the parent relation;
+	 * the executor must apply all these restrictions during the scan.
+	 * Fix regproc ids in the restriction clauses.
 	 */
-	scan_clauses = fix_opids(get_actual_clauses(best_path->loc_restrictinfo));
+	scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo));
 
 	switch (best_path->pathtype)
 	{
@@ -245,8 +241,7 @@ create_join_node(JoinPath *best_path, List *tlist)
 				 best_path->path.pathtype);
 	}
 
-#if 0
-
+#ifdef NOT_USED
 	/*
 	 * * Expensive function pullups may have pulled local predicates *
 	 * into this path node.  Put them in the qpqual of the plan node. *
@@ -282,11 +277,10 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
 	List	   *temp;
 
 	temp = best_path->parent->relids;
-	if (temp == NULL)
-		elog(ERROR, "scanrelid is empty");
-	else
-		scan_relid = (Index) lfirsti(temp);		/* ??? who takes care of
-												 * lnext? - ay */
+	/* there should be exactly one base rel involved... */
+	Assert(length(temp) == 1);
+	scan_relid = (Index) lfirsti(temp);
+
 	scan_node = make_seqscan(tlist,
 							 scan_clauses,
 							 scan_relid,
@@ -319,6 +313,9 @@ create_indexscan_node(IndexPath *best_path,
 	IndexScan  *scan_node;
 	bool		lossy = false;
 
+	/* there should be exactly one base rel involved... */
+	Assert(length(best_path->path.parent->relids) == 1);
+
 	/* check and see if any indices are lossy */
 	foreach(ixid, best_path->indexid)
 	{
@@ -345,9 +342,9 @@ create_indexscan_node(IndexPath *best_path,
 	 * lossy indices the indxqual predicates need to be double-checked
 	 * after the index fetches the best-guess tuples.
 	 *
-	 * There should not be any clauses in scan_clauses that duplicate
-	 * expressions checked by the index, but just in case, we will
-	 * get rid of them via set_difference.
+	 * Since the indexquals were generated from the restriction clauses
+	 * given by scan_clauses, there will normally be some duplications
+	 * between the lists.  Get rid of the duplicates, then add back if lossy.
 	 */
 	if (length(indxqual) > 1)
 	{
@@ -387,9 +384,8 @@ create_indexscan_node(IndexPath *best_path,
 		qpqual = NIL;
 
 	/*
-	 * Fix opids in the completed indxqual.  We don't want to do this sooner
-	 * since it would screw up the set_difference calcs above.  Really,
-	 * this ought to only happen at final exit from the planner...
+	 * Fix opids in the completed indxqual.
+	 * XXX this ought to only happen at final exit from the planner...
 	 */
 	indxqual = fix_opids(indxqual);
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 442ebad1e7b5582a323ffaa2116826a0d29957ba..de9ef509382a72cf0274565d6da5c9fc42c21360 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.42 1999/07/25 23:07:25 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.43 1999/07/27 03:51:04 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -293,8 +293,9 @@ make_andclause(List *andclauses)
 
 /*
  * Sometimes (such as in the result of cnfify), we use lists of expression
- * nodes with implicit AND semantics.  This function converts back to an
- * explicit-AND representation.
+ * nodes with implicit AND semantics.  These functions convert between an
+ * AND-semantics expression list and the ordinary representation of a
+ * boolean expression.
  */
 Expr *
 make_ands_explicit(List *andclauses)
@@ -307,6 +308,17 @@ make_ands_explicit(List *andclauses)
 		return make_andclause(andclauses);
 }
 
+List *
+make_ands_implicit(Expr *clause)
+{
+	if (clause == NULL)
+		return NIL;
+	else if (and_clause((Node *) clause))
+		return clause->args;
+	else
+		return lcons(clause, NIL);
+}
+
 /*****************************************************************************
  *		CASE clause functions
  *****************************************************************************/
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 658ee57baaa667e78eb9daa296d942696863634f..aa0aedb453055209ddd9a8db4749af00bff1fd7e 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.48 1999/07/25 23:07:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.49 1999/07/27 03:51:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "optimizer/keys.h"
 #include "optimizer/ordering.h"
 #include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
 #include "parser/parsetree.h"
@@ -288,22 +289,12 @@ create_seqscan_path(RelOptInfo *rel)
 	pathnode->pathorder->ord.sortop = NULL;
 	pathnode->pathkeys = NIL;
 
-	/*
-	 * copy restrictinfo list into path for expensive function processing
-	 * JMH, 7/7/92
-	 */
-	pathnode->loc_restrictinfo = (List *) copyObject((Node *) rel->restrictinfo);
-
 	if (rel->relids != NULL)
 		relid = lfirsti(rel->relids);
 
 	pathnode->path_cost = cost_seqscan(relid,
 									   rel->pages, rel->tuples);
-	/* add in expensive functions cost!  -- JMH, 7/7/92 */
-#ifdef NOT_USED
-	if (XfuncMode != XFUNC_OFF)
-		pathnode->path_cost += xfunc_get_path_cost(pathnode);
-#endif
+
 	return pathnode;
 }
 
@@ -344,13 +335,6 @@ create_index_path(Query *root,
 	pathnode->indexkeys = index->indexkeys;
 	pathnode->indexqual = NIL;
 
-	/*
-	 * copy restrictinfo list into path for expensive function processing
-	 * JMH, 7/7/92
-	 */
-	pathnode->path.loc_restrictinfo = set_difference((List *) copyObject((Node *) rel->restrictinfo),
-													 restriction_clauses);
-
 	/*
 	 * The index must have an ordering for the path to have (ordering)
 	 * keys, and vice versa.
@@ -403,6 +387,8 @@ create_index_path(Query *root,
 		Cost		clausesel;
 
 		indexquals = get_actual_clauses(restriction_clauses);
+		/* expand special operators to indexquals the executor can handle */
+		indexquals = expand_indexqual_conditions(indexquals);
 
 		index_selectivity(root,
 						  lfirsti(rel->relids),
@@ -425,20 +411,18 @@ create_index_path(Query *root,
 		 * Set selectivities of clauses used with index to the selectivity
 		 * of this index, subdividing the selectivity equally over each of
 		 * the clauses.
+		 *
 		 * XXX Can this divide the selectivities in a better way?
+		 *
 		 * XXX In fact, why the heck are we doing this at all?  We already
-		 * set the cost for the indexpath.
+		 * set the cost for the indexpath, and it's far from obvious that
+		 * the selectivity of the path should have any effect on estimates
+		 * made for other contexts...
 		 */
 		clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
 		set_clause_selectivities(restriction_clauses, clausesel);
 	}
 
-#ifdef NOT_USED
-	/* add in expensive functions cost!  -- JMH, 7/7/92 */
-	if (XfuncMode != XFUNC_OFF)
-		pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
-#endif
-
 	return pathnode;
 }
 
@@ -473,7 +457,6 @@ create_nestloop_path(RelOptInfo *joinrel,
 	pathnode->path.pathkeys = pathkeys;
 	pathnode->path.joinid = NIL;
 	pathnode->path.outerjoincost = (Cost) 0.0;
-	pathnode->path.loc_restrictinfo = NIL;
 	pathnode->path.pathorder = makeNode(PathOrder);
 
 	if (pathkeys)
@@ -497,11 +480,7 @@ create_nestloop_path(RelOptInfo *joinrel,
 											 page_size(outer_rel->size,
 													   outer_rel->width),
 											 IsA(inner_path, IndexPath));
-	/* add in expensive function costs -- JMH 7/7/92 */
-#ifdef NOT_USED
-	if (XfuncMode != XFUNC_OFF)
-		pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
-#endif
+
 	return pathnode;
 }
 
@@ -550,7 +529,6 @@ create_mergejoin_path(RelOptInfo *joinrel,
 	pathnode->jpath.path.pathorder->ordtype = MERGE_ORDER;
 	pathnode->jpath.path.pathorder->ord.merge = order;
 	pathnode->path_mergeclauses = mergeclauses;
-	pathnode->jpath.path.loc_restrictinfo = NIL;
 	pathnode->outersortkeys = outersortkeys;
 	pathnode->innersortkeys = innersortkeys;
 	pathnode->jpath.path.path_cost = cost_mergejoin(outer_path->path_cost,
@@ -561,11 +539,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
 													innersize,
 													outerwidth,
 													innerwidth);
-	/* add in expensive function costs -- JMH 7/7/92 */
-#ifdef NOT_USED
-	if (XfuncMode != XFUNC_OFF)
-		pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
-#endif
+
 	return pathnode;
 }
 
@@ -608,7 +582,6 @@ create_hashjoin_path(RelOptInfo *joinrel,
 	pathnode->jpath.outerjoinpath = outer_path;
 	pathnode->jpath.innerjoinpath = inner_path;
 	pathnode->jpath.pathinfo = joinrel->restrictinfo;
-	pathnode->jpath.path.loc_restrictinfo = NIL;
 	pathnode->jpath.path.pathkeys = pathkeys;
 	pathnode->jpath.path.pathorder = makeNode(PathOrder);
 	pathnode->jpath.path.pathorder->ordtype = SORTOP_ORDER;
@@ -625,10 +598,6 @@ create_hashjoin_path(RelOptInfo *joinrel,
 												   innerkeys,
 												   outersize, innersize,
 												 outerwidth, innerwidth);
-	/* add in expensive function costs -- JMH 7/7/92 */
-#ifdef NOT_USED
-	if (XfuncMode != XFUNC_OFF)
-		pathnode->path_cost += xfunc_get_path_cost((Path *) pathnode);
-#endif
+
 	return pathnode;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ec41d7efce900b4549b3dc0cdfd6b1390f215aa2..60ff7a6ef259f266e7e10db93f41e244f3c04a55 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.94 1999/07/20 00:18:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.95 1999/07/27 03:51:06 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -76,7 +76,6 @@ static void mapTargetColumns(List *source, List *target);
 static List *makeConstantList( A_Const *node);
 static char *FlattenStringList(List *list);
 static char *fmtId(char *rawid);
-static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr);
 static void param_type_init(Oid *typev, int nargs);
 static Node *doNegate(Node *n);
 
@@ -3724,9 +3723,9 @@ a_expr:  attr
 		| '(' a_expr_or_null ')'
 				{	$$ = $2; }
 		| a_expr Op a_expr
-				{	$$ = makeIndexable($2,$1,$3);	}
+				{	$$ = makeA_Expr(OP, $2, $1, $3);	}
 		| a_expr LIKE a_expr
-				{	$$ = makeIndexable("~~", $1, $3); }
+				{	$$ = makeA_Expr(OP, "~~", $1, $3); }
 		| a_expr NOT LIKE a_expr
 				{	$$ = makeA_Expr(OP, "!~~", $1, $4); }
 		| Op a_expr
@@ -4376,7 +4375,7 @@ b_expr:  attr
 		| '(' a_expr ')'
 				{	$$ = $2; }
 		| b_expr Op b_expr
-				{	$$ = makeIndexable($2,$1,$3);	}
+				{	$$ = makeA_Expr(OP, $2,$1,$3);	}
 		| Op b_expr
 				{	$$ = makeA_Expr(OP, $1, NULL, $2); }
 		| b_expr Op
@@ -5197,157 +5196,6 @@ mapTargetColumns(List *src, List *dst)
 	return;
 } /* mapTargetColumns() */
 
-static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr)
-{
-	Node *result = NULL;
-	
-	/* we do this so indexes can be used */
-	if (strcmp(opname,"~") == 0 ||
-		strcmp(opname,"~*") == 0)
-	{
-		if (nodeTag(rexpr) == T_A_Const &&
-		   ((A_Const *)rexpr)->val.type == T_String &&
-		   ((A_Const *)rexpr)->val.val.str[0] == '^')
-		{
-			A_Const *n = (A_Const *)rexpr;
-			char *match_least = palloc(strlen(n->val.val.str)+2);
-			char *match_most = palloc(strlen(n->val.val.str)+2);
-			int pos, match_pos=0;
-			bool found_special = false;
-
-			/* Cannot optimize if unquoted | { } is present in pattern */
-			for (pos = 1; n->val.val.str[pos]; pos++)
-			{
-				if (n->val.val.str[pos] == '|' ||
-				    n->val.val.str[pos] == '{' ||
-				    n->val.val.str[pos] == '}')
-				{
-					found_special = true;
-					break;
-				}
-		     	if (n->val.val.str[pos] == '\\')
-				{
-					pos++;
-					if (n->val.val.str[pos] == '\0')
-						break;
-				}
-			}
-
-			if (!found_special)
-			{
-				/* note start at pos 1 to skip leading ^ */
-				for (pos = 1; n->val.val.str[pos]; pos++)
-				{
-					if (n->val.val.str[pos] == '.' ||
-						n->val.val.str[pos] == '?' ||
-						n->val.val.str[pos] == '*' ||
-						n->val.val.str[pos] == '[' ||
-						n->val.val.str[pos] == '$' ||
-						(strcmp(opname,"~*") == 0 && isalpha(n->val.val.str[pos])))
-			     		break;
-			     	if (n->val.val.str[pos] == '\\')
-					{
-						pos++;
-						if (n->val.val.str[pos] == '\0')
-							break;
-					}
-					match_least[match_pos] = n->val.val.str[pos];
-					match_most[match_pos++] = n->val.val.str[pos];
-				}
-	
-				if (match_pos != 0)
-				{
-					A_Const *least = makeNode(A_Const);
-					A_Const *most = makeNode(A_Const);
-					
-					/* make strings to be used in index use */
-					match_least[match_pos] = '\0';
-					match_most[match_pos] = '\377';
-					match_most[match_pos+1] = '\0';
-					least->val.type = T_String;
-					least->val.val.str = match_least;
-					most->val.type = T_String;
-					most->val.val.str = match_most;
-#ifdef USE_LOCALE
-					result = makeA_Expr(AND, NULL,
-							makeA_Expr(OP, "~", lexpr, rexpr),
-							makeA_Expr(OP, ">=", lexpr, (Node *)least));
-#else
-					result = makeA_Expr(AND, NULL,
-							makeA_Expr(OP, "~", lexpr, rexpr),
-							makeA_Expr(AND, NULL,
-								makeA_Expr(OP, ">=", lexpr, (Node *)least),
-								makeA_Expr(OP, "<=", lexpr, (Node *)most)));
-#endif
-				}
-			}
-		}
-	}
-	else if (strcmp(opname,"~~") == 0)
-	{
-		if (nodeTag(rexpr) == T_A_Const &&
-		   ((A_Const *)rexpr)->val.type == T_String)
-		{
-			A_Const *n = (A_Const *)rexpr;
-			char *match_least = palloc(strlen(n->val.val.str)+2);
-			char *match_most = palloc(strlen(n->val.val.str)+2);
-			int pos, match_pos=0;
-	
-			for (pos = 0; n->val.val.str[pos]; pos++)
-			{
-				/* % and _ are wildcard characters in LIKE */
-				if (n->val.val.str[pos] == '%' ||
-					n->val.val.str[pos] == '_')
-					break;
-				/* Backslash quotes the next character */
-				if (n->val.val.str[pos] == '\\')
-				{
-					pos++;
-					if (n->val.val.str[pos] == '\0')
-						break;
-				}
-				/*
-				 * NOTE: this code used to think that %% meant a literal %,
-				 * but textlike() itself does not think that, and the SQL92
-				 * spec doesn't say any such thing either.
-				 */
-				match_least[match_pos] = n->val.val.str[pos];
-				match_most[match_pos++] = n->val.val.str[pos];
-			}
-	
-			if (match_pos != 0)
-			{
-				A_Const *least = makeNode(A_Const);
-				A_Const *most = makeNode(A_Const);
-				
-				/* make strings to be used in index use */
-				match_least[match_pos] = '\0';
-				match_most[match_pos] = '\377';
-				match_most[match_pos+1] = '\0';
-				least->val.type = T_String;
-				least->val.val.str = match_least;
-				most->val.type = T_String;
-				most->val.val.str = match_most;
-#ifdef USE_LOCALE
-				result = makeA_Expr(AND, NULL,
-						makeA_Expr(OP, "~~", lexpr, rexpr),
-						makeA_Expr(OP, ">=", lexpr, (Node *)least));
-#else
-				result = makeA_Expr(AND, NULL,
-						makeA_Expr(OP, "~~", lexpr, rexpr),
-						makeA_Expr(AND, NULL,
-							makeA_Expr(OP, ">=", lexpr, (Node *)least),
-							makeA_Expr(OP, "<=", lexpr, (Node *)most)));
-#endif
-			}
-		}
-	}
-	
-	if (result == NULL)
-		result = makeA_Expr(OP, opname, lexpr, rexpr);
-	return result;
-} /* makeIndexable() */
-
 
 /* xlateSqlFunc()
  * Convert alternate type names to internal Postgres types.
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 1dec2cfcd97156f5c302f976c9ef0a7a933372a3..e22e1e5afb7fda3c91903593ff578c4de684da8b 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.57 1999/05/25 16:13:46 momjian Exp $
+ * $Id: pg_operator.h,v 1.58 1999/07/27 03:51:11 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -271,7 +271,6 @@ DATA(insert OID = 1283 (  ";"		PGUID 0 l t f	0 701 701	0	0	0	0 dlog1 - - ));
 DATA(insert OID = 1284 (  "|"		PGUID 0 l t f	0 704 702	0	0	0	0 intervalstart - - ));
 DATA(insert OID = 606 (  "<#>"		PGUID 0 b t f 702 702 704	0	0	0	0 mktinterval - - ));
 DATA(insert OID = 607 (  "="	   PGUID 0 b t t  26  26  16 607 608 609 609 oideq eqsel eqjoinsel ));
-#define OIDEqualOperator 607	/* XXX planner/prep/semanopt.c crock */
 DATA(insert OID = 608 (  "<>"	   PGUID 0 b t f  26  26  16 608 607  0  0 oidne neqsel neqjoinsel ));
 
 DATA(insert OID = 644 (  "<>"	   PGUID 0 b t f  30  30  16 644 649   0   0 oid8ne neqsel neqjoinsel ));
@@ -301,7 +300,6 @@ DATA(insert OID = 624 (  "<="	   PGUID 0 b t f  700  700	16 625 623	0 0 float4le
 DATA(insert OID = 625 (  ">="	   PGUID 0 b t f  700  700	16 624 622	0 0 float4ge intgtsel intgtjoinsel ));
 DATA(insert OID = 626 (  "!!="	   PGUID 0 b t f  23   19	16 0   0	0	0	int4notin - - ));
 DATA(insert OID = 627 (  "!!="	   PGUID 0 b t f  26   19	16 0   0	0	0	oidnotin - - ));
-#define OIDNotInOperator 627	/* XXX planner/prep/semanopt.c crock */
 DATA(insert OID = 630 (  "<>"	   PGUID 0 b t f  18  18  16 630  92  0 0 charne neqsel neqjoinsel ));
 
 DATA(insert OID = 631 (  "<"	   PGUID 0 b t f  18  18  16 633 634  0 0 charlt intltsel intltjoinsel ));
@@ -315,8 +313,10 @@ DATA(insert OID = 637 (  "*"	   PGUID 0 b t f  18  18  18 0 0  0 0 charmul - - )
 DATA(insert OID = 638 (  "/"	   PGUID 0 b t f  18  18  18 0 0  0 0 chardiv - - ));
 
 DATA(insert OID = 639 (  "~"	   PGUID 0 b t f  19  25  16 0 640	0 0 nameregexeq eqsel eqjoinsel ));
+#define OID_NAME_REGEXEQ_OP		639
 DATA(insert OID = 640 (  "!~"	   PGUID 0 b t f  19  25  16 0 639	0 0 nameregexne neqsel neqjoinsel ));
 DATA(insert OID = 641 (  "~"	   PGUID 0 b t f  25  25  16 0 642	0 0 textregexeq eqsel eqjoinsel ));
+#define OID_TEXT_REGEXEQ_OP		641
 DATA(insert OID = 642 (  "!~"	   PGUID 0 b t f  25  25  16 0 641	0 0 textregexne neqsel neqjoinsel ));
 DATA(insert OID = 643 (  "<>"	   PGUID 0 b t f  19  19  16 643 93 0 0 namene neqsel neqjoinsel ));
 DATA(insert OID = 654 (  "||"	   PGUID 0 b t f  25  25  25   0 0	0 0 textcat - - ));
@@ -436,6 +436,7 @@ DATA(insert OID =  979 (  "||"	   PGUID 0 b t f 1043 1043 1043    0  0 0 0 textc
 
 DATA(insert OID = 1054 ( "="	   PGUID 0 b t t 1042 1042	 16 1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
 DATA(insert OID = 1055 ( "~"	   PGUID 0 b t f 1042	25	 16    0 1056  0 0 textregexeq eqsel eqjoinsel ));
+#define OID_BPCHAR_REGEXEQ_OP		1055
 DATA(insert OID = 1056 ( "!~"	   PGUID 0 b t f 1042	25	 16    0 1055  0 0 textregexne neqsel neqjoinsel ));
 DATA(insert OID = 1057 ( "<>"	   PGUID 0 b t f 1042 1042	 16 1057 1054  0 0 bpcharne neqsel neqjoinsel ));
 DATA(insert OID = 1058 ( "<"	   PGUID 0 b t f 1042 1042	 16 1060 1061  0 0 bpcharlt intltsel intltjoinsel ));
@@ -445,6 +446,7 @@ DATA(insert OID = 1061 ( ">="	   PGUID 0 b t f 1042 1042	 16 1059 1058  0 0 bpch
 
 DATA(insert OID = 1062 ( "="	   PGUID 0 b t t 1043 1043	16	1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
 DATA(insert OID = 1063 ( "~"	   PGUID 0 b t f 1043	25	16 0 1064  0 0 textregexeq eqsel eqjoinsel ));
+#define OID_VARCHAR_REGEXEQ_OP		1063
 DATA(insert OID = 1064 ( "!~"	   PGUID 0 b t f 1043	25	16 0 1063  0 0 textregexne neqsel neqjoinsel ));
 DATA(insert OID = 1065 ( "<>"	   PGUID 0 b t f 1043 1043	16 1065 1062  0 0 varcharne neqsel neqjoinsel ));
 DATA(insert OID = 1066 ( "<"	   PGUID 0 b t f 1043 1043	16 1068 1069  0 0 varcharlt intltsel intltjoinsel ));
@@ -501,22 +503,30 @@ DATA(insert OID = 1137 (  "="		PGUID 0 b t t 26 23 16 1136 0 0 0 oideqint4 eqsel
 
 /* LIKE hacks by Keith Parks. */
 DATA(insert OID = 1207 (  "~~"	  PGUID 0 b t f  19   25  16 0 1208 0 0 namelike eqsel eqjoinsel ));
+#define OID_NAME_LIKE_OP		1207
 DATA(insert OID = 1208 (  "!~~"   PGUID 0 b t f  19   25  16 0 1207 0 0 namenlike neqsel neqjoinsel ));
 DATA(insert OID = 1209 (  "~~"	  PGUID 0 b t f  25   25  16 0 1210 0 0 textlike eqsel eqjoinsel ));
+#define OID_TEXT_LIKE_OP		1209
 DATA(insert OID = 1210 (  "!~~"   PGUID 0 b t f  25   25  16 0 1209 0 0 textnlike neqsel neqjoinsel ));
 DATA(insert OID = 1211 (  "~~"	  PGUID 0 b t f  1042 25  16 0 1212 0 0 textlike eqsel eqjoinsel ));
+#define OID_BPCHAR_LIKE_OP		1211
 DATA(insert OID = 1212 (  "!~~"   PGUID 0 b t f  1042 25  16 0 1211 0 0 textnlike neqsel neqjoinsel ));
 DATA(insert OID = 1213 (  "~~"	  PGUID 0 b t f  1043 25  16 0 1214 0 0 textlike eqsel eqjoinsel ));
+#define OID_VARCHAR_LIKE_OP		1213
 DATA(insert OID = 1214 (  "!~~"   PGUID 0 b t f  1043 25  16 0 1213 0 0 textnlike neqsel neqjoinsel ));
 
 /* case-insensitive LIKE hacks */
 DATA(insert OID = 1226 (  "~*"		 PGUID 0 b t f	19	25	16 0 1227  0 0 nameicregexeq eqsel eqjoinsel ));
+#define OID_NAME_ICREGEXEQ_OP		1226
 DATA(insert OID = 1227 (  "!~*"		 PGUID 0 b t f	19	25	16 0 1226  0 0 nameicregexne neqsel neqjoinsel ));
 DATA(insert OID = 1228 (  "~*"		 PGUID 0 b t f	25	25	16 0 1229  0 0 texticregexeq eqsel eqjoinsel ));
+#define OID_TEXT_ICREGEXEQ_OP		1228
 DATA(insert OID = 1229 (  "!~*"		 PGUID 0 b t f	25	25	16 0 1228  0 0 texticregexne neqsel neqjoinsel ));
 DATA(insert OID = 1232 (  "~*"		PGUID 0 b t f  1043  25  16 0 1233	0 0 texticregexeq eqsel eqjoinsel ));
+#define OID_VARCHAR_ICREGEXEQ_OP		1232
 DATA(insert OID = 1233 ( "!~*"		PGUID 0 b t f  1043  25  16 0 1232	0 0 texticregexne neqsel neqjoinsel ));
 DATA(insert OID = 1234 (  "~*"		PGUID 0 b t f  1042  25  16 0 1235	0 0 texticregexeq eqsel eqjoinsel ));
+#define OID_BPCHAR_ICREGEXEQ_OP		1234
 DATA(insert OID = 1235 ( "!~*"		PGUID 0 b t f  1042  25  16 0 1234	0 0 texticregexne neqsel neqjoinsel ));
 
 DATA(insert OID = 1300 (  "="		PGUID 0 b t t  1296 1296 16 1300 1301 1302 1302 timestampeq eqsel eqjoinsel ));
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 0402fb8fb9ee1314cc7d46f81fea01aac1cfab43..4db2e9cea445b74b70f52ecd69d78fbf39d0f6cc 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.36 1999/07/25 17:53:26 tgl Exp $
+ * $Id: relation.h,v 1.37 1999/07/27 03:51:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -148,7 +148,6 @@ typedef struct Path
 								 * information. */
 	Cost		outerjoincost;
 	Relids		joinid;
-	List	   *loc_restrictinfo;
 } Path;
 
 /*----------
@@ -292,7 +291,7 @@ typedef struct Iter
  *	cinfo -- if NULL, this stream node referes to the path node.
  *			  Otherwise this is a pointer to the current clause.
  *	clausetype -- whether cinfo is in loc_restrictinfo or pathinfo in the
- *			  path node
+ *			  path node (XXX this is now used only by dead code...)
  *	upstream -- linked list pointer upwards
  *	downstream -- ditto, downwards
  *	groupup -- whether or not this node is in a group with the node upstream
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 748c0d46e9fd9818eb79aae2e35e08771b0c411b..ad6976760a5f5877972642b955f88de66bb85bfc 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.23 1999/07/25 23:07:23 tgl Exp $
+ * $Id: clauses.h,v 1.24 1999/07/27 03:51:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@ extern Expr *get_notclausearg(Expr *notclause);
 extern bool and_clause(Node *clause);
 extern Expr *make_andclause(List *andclauses);
 extern Expr *make_ands_explicit(List *andclauses);
+extern List *make_ands_implicit(Expr *clause);
 
 extern bool case_clause(Node *clause);
 
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index a26085d05e26265a3abca7b2a7dcf145f7cecf72..75d9e328458afb81ec7b32d4732e5fa214b4bb8f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.31 1999/07/15 15:21:22 momjian Exp $
+ * $Id: paths.h,v 1.32 1999/07/27 03:51:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@ extern RelOptInfo *make_one_rel(Query *root, List *rels);
 extern List *create_index_paths(Query *root, RelOptInfo *rel, List *indices,
 				   List *restrictinfo_list,
 				   List *joininfo_list);
+extern List *expand_indexqual_conditions(List *indexquals);
 
 /*
  * joinpath.h