diff --git a/contrib/array/array_iterator.c b/contrib/array/array_iterator.c
index d2a9a3271e632232c4e1e3c650b603248461100d..9504f7527795862893ea4f1321c3c52e15c0554c 100644
--- a/contrib/array/array_iterator.c
+++ b/contrib/array/array_iterator.c
@@ -145,43 +145,6 @@ array_all_textregexeq(ArrayType *array, void *value)
 						  array, (Datum) value);
 }
 
-/*
- * Iterator functions for type _varchar. Note that the regexp
- * operators take the second argument of type text.
- */
-
-int32
-array_varchareq(ArrayType *array, void *value)
-{
-	return array_iterator(F_VARCHAREQ,
-						  0,	/* logical or */
-						  array, (Datum) value);
-}
-
-int32
-array_all_varchareq(ArrayType *array, void *value)
-{
-	return array_iterator(F_VARCHAREQ,
-						  1,	/* logical and */
-						  array, (Datum) value);
-}
-
-int32
-array_varcharregexeq(ArrayType *array, void *value)
-{
-	return array_iterator(F_TEXTREGEXEQ,
-						  0,	/* logical or */
-						  array, (Datum) value);
-}
-
-int32
-array_all_varcharregexeq(ArrayType *array, void *value)
-{
-	return array_iterator(F_TEXTREGEXEQ,
-						  1,	/* logical and */
-						  array, (Datum) value);
-}
-
 /*
  * Iterator functions for type _bpchar. Note that the regexp
  * operators take the second argument of type text.
diff --git a/contrib/array/array_iterator.h b/contrib/array/array_iterator.h
index c85d68f27ac60463a97b155520ef0b6a6d0cd453..75cfba07dd7596ac9415f9fb5a0c5ad91f163d28 100644
--- a/contrib/array/array_iterator.h
+++ b/contrib/array/array_iterator.h
@@ -9,11 +9,6 @@ int32		array_all_texteq(ArrayType *array, void *value);
 int32		array_textregexeq(ArrayType *array, void *value);
 int32		array_all_textregexeq(ArrayType *array, void *value);
 
-int32		array_varchareq(ArrayType *array, void *value);
-int32		array_all_varchareq(ArrayType *array, void *value);
-int32		array_varcharregexeq(ArrayType *array, void *value);
-int32		array_all_varcharregexeq(ArrayType *array, void *value);
-
 int32		array_bpchareq(ArrayType *array, void *value);
 int32		array_all_bpchareq(ArrayType *array, void *value);
 int32		array_bpcharregexeq(ArrayType *array, void *value);
diff --git a/contrib/array/array_iterator.sql.in b/contrib/array/array_iterator.sql.in
index 4108a63eafd524d70108159405db6e70cb0f2ea1..2d89f2c9872d2e2a0784131bf4d135725d0bca6a 100644
--- a/contrib/array/array_iterator.sql.in
+++ b/contrib/array/array_iterator.sql.in
@@ -55,59 +55,6 @@ CREATE OPERATOR **~ (
 );
 
 
--- define the array operators *=, **=, *~ and **~ for type _varchar
---
--- NOTE: "varchar" is also a reserved word and must be quoted.
---
-CREATE OR REPLACE FUNCTION array_varchareq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_all_varchareq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_varcharregexeq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-CREATE OR REPLACE FUNCTION array_all_varcharregexeq(_varchar, varchar)
-RETURNS bool
-AS 'MODULE_PATHNAME' 
-LANGUAGE 'C' IMMUTABLE STRICT;
-
-DROP OPERATOR *=(_varchar,"varchar");
-CREATE OPERATOR *= (
-	LEFTARG=_varchar, 
-	RIGHTARG="varchar", 
-	PROCEDURE=array_varchareq
-);
-
-DROP OPERATOR **=(_varchar,"varchar");
-CREATE OPERATOR **= (
-	LEFTARG=_varchar,
-	RIGHTARG="varchar",
-	PROCEDURE=array_all_varchareq
-);
-
-DROP OPERATOR *~(_varchar,"varchar");
-CREATE OPERATOR *~ (
-	LEFTARG=_varchar,
-	RIGHTARG="varchar",
-	PROCEDURE=array_varcharregexeq
-);
-
-DROP OPERATOR **~(_varchar,"varchar");
-CREATE OPERATOR **~ (
-	LEFTARG=_varchar,
-	RIGHTARG="varchar",
-	PROCEDURE=array_all_varcharregexeq
-);
-
-
 -- define the array operators *=, **=, *~ and **~ for type _bpchar
 --
 CREATE OR REPLACE FUNCTION array_bpchareq(_bpchar, bpchar)
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index ea6fe8a72174d09080b73501a20e4965e087ab10..3250595964411a173d730e0ce42c554bc0a9b3ef 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.189 2003/05/22 18:31:45 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.190 2003/05/26 00:11:27 tgl Exp $
 -->
 
 <appendix id="release">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+CHAR(n) to TEXT conversion automatically strips trailing blanks
 Pattern matching operations can use indexes regardless of locale
 New frontend/backend protocol supports many long-requested features
 SET AUTOCOMMIT TO OFF is no longer supported
diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml
index 0a85ea1230f61596760ac0f78649991ab3517a19..fa59aba0fea747f24b00314c41a962bbc0849037 100644
--- a/doc/src/sgml/typeconv.sgml
+++ b/doc/src/sgml/typeconv.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/typeconv.sgml,v 1.30 2003/03/25 16:15:38 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/typeconv.sgml,v 1.31 2003/05/26 00:11:27 tgl Exp $
 -->
 
 <chapter Id="typeconv">
@@ -45,7 +45,7 @@ mixed-type expressions to be meaningful even with user-defined types.
 <para>
 The <productname>PostgreSQL</productname> scanner/parser decodes lexical
 elements into only five fundamental categories: integers, floating-point numbers, strings,
-names, and key words.  Most extended types are first classified as
+names, and key words.  Constants of most non-numeric types are first classified as
 strings. The <acronym>SQL</acronym> language definition allows specifying type
 names with strings, and this mechanism can be used in
 <productname>PostgreSQL</productname> to start the parser down the correct
@@ -134,8 +134,8 @@ The system catalogs store information about which conversions, called
 perform those conversions.  Additional casts can be added by the user
 with the <command>CREATE CAST</command> command.  (This is usually
 done in conjunction with defining new data types.  The set of casts
-between the built-in types has been carefully crafted and should not
-be altered.)
+between the built-in types has been carefully crafted and is best not
+altered.)
 </para>
 
 <para>
@@ -144,8 +144,8 @@ at proper behavior for <acronym>SQL</acronym> standard types. There are
 several basic <firstterm>type categories</firstterm> defined: <type>boolean</type>,
 <type>numeric</type>, <type>string</type>, <type>bitstring</type>, <type>datetime</type>, <type>timespan</type>, <type>geometric</type>, <type>network</type>,
 and user-defined. Each category, with the exception of user-defined, has
-a <firstterm>preferred type</firstterm> which is preferentially selected
-when there is ambiguity.
+one or more <firstterm>preferred types</firstterm> which are preferentially
+selected when there is ambiguity.
 In the user-defined category, each type is its own preferred type.
 Ambiguous expressions (those with multiple candidate parsing solutions)
 can therefore often be resolved when there are multiple possible built-in types, but
@@ -175,7 +175,8 @@ be converted to a user-defined type (of course, only if conversion is necessary)
 <para>
 User-defined types are not related. Currently, <productname>PostgreSQL</productname>
 does not have information available to it on relationships between types, other than
-hardcoded heuristics for built-in types and implicit relationships based on available functions.
+hardcoded heuristics for built-in types and implicit relationships based on
+available functions and casts.
 </para>
 </listitem>
 
@@ -203,14 +204,15 @@ should use this new function and will no longer do the implicit conversion using
 <title>Operators</title>
 
   <para>
-   The operand types of an operator invocation are resolved following
+   The specific operator to be used in an operator invocation is determined
+   by following
    the procedure below.  Note that this procedure is indirectly affected
    by the precedence of the involved operators.  See <xref
    linkend="sql-precedence"> for more information.
   </para>
 
 <procedure>
-<title>Operand Type Resolution</title>
+<title>Operator Type Resolution</title>
 
 <step performance="required">
 <para>
@@ -271,22 +273,16 @@ candidate remains, use it; else continue to the next step.
 <step performance="required">
 <para>
 Run through all candidates and keep those with the most exact matches
-on input types.  Keep all candidates if none have any exact matches.
+on input types.  (Domain types are considered the same as their base type
+for this purpose.)  Keep all candidates if none have any exact matches.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-Run through all candidates and keep those with the most exact or
-binary-compatible matches on input types.  Keep all candidates if none have
-any exact or binary-compatible matches.
-If only one candidate remains, use it; else continue to the next step.
-</para>
-</step>
-<step performance="required">
-<para>
-Run through all candidates and keep those that accept preferred types at
-the most positions where type conversion will be required.
+Run through all candidates and keep those that accept preferred types (of the
+input datatype's type category) at the most positions where type conversion
+will be required.
 Keep all candidates if none accept preferred types.
 If only one candidate remains, use it; else continue to the next step.
 </para>
@@ -295,12 +291,13 @@ If only one candidate remains, use it; else continue to the next step.
 <para>
 If any input arguments are <type>unknown</type>, check the type
 categories accepted at those argument positions by the remaining
-candidates.  At each position, select the <literal>string</literal> category if any
+candidates.  At each position, select the <type>string</type> category
+if any
 candidate accepts that category.  (This bias towards string is appropriate
 since an unknown-type literal does look like a string.) Otherwise, if
 all the remaining candidates accept the same type category, select that
 category; otherwise fail because the correct choice cannot be deduced
-without more clues.  Now discard operator
+without more clues.  Now discard
 candidates that do not accept the selected type category.  Furthermore,
 if any candidate accepts a preferred type at a given argument position,
 discard candidates that accept non-preferred types for that argument.
@@ -455,12 +452,12 @@ SELECT CAST('20' AS int8) ! AS "factorial";
 <title>Functions</title>
 
   <para>
-   The argument types of function calls are resolved according to the
-   following steps.
+   The specific function to be used in a function invocation is determined
+   according to the following steps.
   </para>
 
 <procedure>
-<title>Function Argument Type Resolution</title>
+<title>Function Type Resolution</title>
 
 <step performance="required">
 <para>
@@ -523,29 +520,24 @@ candidate remains, use it; else continue to the next step.
 <step performance="required">
 <para>
 Run through all candidates and keep those with the most exact matches
-on input types.  Keep all candidates if none have any exact matches.
-If only one candidate remains, use it; else continue to the next step.
-</para>
-</step>
-<step performance="required">
-<para>
-Run through all candidates and keep those with the most exact or
-binary-compatible matches on input types.  Keep all candidates if none have
-any exact or binary-compatible matches.
+on input types.  (Domain types are considered the same as their base type
+for this purpose.)  Keep all candidates if none have any exact matches.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-Run through all candidates and keep those that accept preferred types at
-the most positions where type conversion will be required.
+Run through all candidates and keep those that accept preferred types (of the
+input datatype's type category) at the most positions where type conversion
+will be required.
 Keep all candidates if none accept preferred types.
 If only one candidate remains, use it; else continue to the next step.
 </para>
 </step>
 <step performance="required">
 <para>
-If any input arguments are <type>unknown</type>, check the type categories accepted
+If any input arguments are <type>unknown</type>, check the type categories
+accepted 
 at those argument positions by the remaining candidates.  At each position,
 select the <type>string</type> category if any candidate accepts that category.
 (This bias towards string
@@ -553,8 +545,8 @@ is appropriate since an unknown-type literal does look like a string.)
 Otherwise, if all the remaining candidates accept the same type category,
 select that category; otherwise fail because
 the correct choice cannot be deduced without more clues.
-Now discard candidates that do not accept the selected type category;
-furthermore, if any candidate accepts a preferred type at a given argument
+Now discard candidates that do not accept the selected type category.
+Furthermore, if any candidate accepts a preferred type at a given argument
 position, discard candidates that accept non-preferred types for that
 argument.
 </para>
@@ -571,6 +563,8 @@ then fail.
 </procedure>
 
 <para>
+Note that the <quote>best match</> rules are identical for operator and
+function type resolution.
 Some examples follow.
 </para>
 
@@ -649,7 +643,8 @@ SELECT substr(CAST (varchar '1234' AS text), 3);
 <para>
 <note>
 <para>
-The parser is aware that <type>text</type> and <type>varchar</type>
+The parser learns from the <structname>pg_cast</> catalog that
+<type>text</type> and <type>varchar</type>
 are binary-compatible, meaning that one can be passed to a function that
 accepts the other without doing any physical conversion.  Therefore, no
 explicit type conversion call is really inserted in this case.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b4da4666f1fcd5238e5fdeb708f193412a9aedbc..4529c30e626acacce405612fda52eccde6676c03 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.139 2003/05/15 19:34:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.140 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "access/nbtree.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
@@ -30,6 +31,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/var.h"
+#include "parser/parse_expr.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
@@ -80,17 +82,18 @@ static bool pred_test_simple_clause(Expr *predicate, Node *clause);
 static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
 static Path *make_innerjoin_index_path(Query *root,
 									   RelOptInfo *rel, IndexOptInfo *index,
-									   List *clausegroup);
+									   List *clausegroups);
 static bool match_index_to_operand(int indexkey, Node *operand,
 					   RelOptInfo *rel, IndexOptInfo *index);
 static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
 					   IndexOptInfo *index);
 static bool match_special_index_operator(Expr *clause, Oid opclass,
 							 bool indexkey_on_left);
-static List *prefix_quals(Node *leftop, Oid expr_op,
+static List *expand_indexqual_condition(Expr *clause, Oid opclass);
+static List *prefix_quals(Node *leftop, Oid opclass,
 			 Const *prefix, Pattern_Prefix_Status pstatus);
-static List *network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop);
-static Oid	find_operator(const char *opname, Oid datatype);
+static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
+								  Datum rightop);
 static Datum string_to_datum(const char *str, Oid datatype);
 static Const *string_to_const(const char *str, Oid datatype);
 
@@ -411,7 +414,7 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
  * Currently we'll end up rechecking both the OR clause and the transferred
  * restriction clause as qpquals.  FIXME someday.)
  *
- * Also, we apply expand_indexqual_conditions() to convert any special
+ * Also, we apply expand_indexqual_condition() to convert any special
  * matching opclauses to indexable operators.
  *
  * The passed-in clause is not changed.
@@ -430,7 +433,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 	 * Extract relevant indexclauses in indexkey order.  This is
 	 * essentially just like group_clauses_by_indexkey() except that the
 	 * input and output are lists of bare clauses, not of RestrictInfo
-	 * nodes.
+	 * nodes, and that we expand special operators immediately.
 	 */
 	do
 	{
@@ -448,13 +451,15 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 				if (match_clause_to_indexkey(rel, index,
 											 curIndxKey, curClass,
 											 subsubclause))
-					clausegroup = lappend(clausegroup, subsubclause);
+					clausegroup = nconc(clausegroup,
+										expand_indexqual_condition(subsubclause,
+																   curClass));
 			}
 		}
 		else if (match_clause_to_indexkey(rel, index,
 										  curIndxKey, curClass,
 										  orsubclause))
-			clausegroup = makeList1(orsubclause);
+			clausegroup = expand_indexqual_condition(orsubclause, curClass);
 
 		/*
 		 * If we found no clauses for this indexkey in the OR subclause
@@ -469,7 +474,9 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 				if (match_clause_to_indexkey(rel, index,
 											 curIndxKey, curClass,
 											 rinfo->clause))
-					clausegroup = lappend(clausegroup, rinfo->clause);
+					clausegroup = nconc(clausegroup,
+										expand_indexqual_condition(rinfo->clause,
+																   curClass));
 			}
 		}
 
@@ -490,7 +497,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 	if (quals == NIL)
 		elog(ERROR, "extract_or_indexqual_conditions: no matching clause");
 
-	return expand_indexqual_conditions(quals);
+	return quals;
 }
 
 
@@ -501,26 +508,23 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 
 /*
  * group_clauses_by_indexkey
- *	  Generates a list of restriction clauses that can be used with an index.
+ *	  Find restriction clauses that can be used with an index.
  *
  * 'rel' is the node of the relation itself.
  * 'index' is a index on 'rel'.
  *
- * Returns a list of all the RestrictInfo nodes for clauses that can be
- * used with this index.
- *
- * The list is ordered by index key.  (This is not depended on by any part
- * of the planner, so far as I can tell; but some parts of the executor
- * do assume that the indxqual list ultimately delivered to the executor
- * is so ordered.  One such place is _bt_orderkeys() in the btree support.
- * Perhaps that ought to be fixed someday --- tgl 7/00)
+ * Returns a list of sublists of RestrictInfo nodes for clauses that can be
+ * used with this index.  Each sublist contains clauses that can be used
+ * with one index key (in no particular order); the top list is ordered by
+ * index key.  (This is depended on by expand_indexqual_conditions().)
  *
  * Note that in a multi-key index, we stop if we find a key that cannot be
  * used with any clause.  For example, given an index on (A,B,C), we might
- * return (C1 C2 C3 C4) if we find that clauses C1 and C2 use column A,
+ * return ((C1 C2) (C3 C4)) if we find that clauses C1 and C2 use column A,
  * clauses C3 and C4 use column B, and no clauses use column C.  But if
- * no clauses match B we will return (C1 C2), whether or not there are
+ * no clauses match B we will return ((C1 C2)), whether or not there are
  * clauses matching column C, because the executor couldn't use them anyway.
+ * Therefore, there are no empty sublists in the result.
  */
 static List *
 group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
@@ -559,20 +563,19 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 		if (clausegroup == NIL)
 			break;
 
-		clausegroup_list = nconc(clausegroup_list, clausegroup);
+		clausegroup_list = lappend(clausegroup_list, clausegroup);
 
 		indexkeys++;
 		classes++;
 
 	} while (!DoneMatchingIndexKeys(indexkeys, classes));
 
-	/* clausegroup_list holds all matched clauses ordered by indexkeys */
 	return clausegroup_list;
 }
 
 /*
  * group_clauses_by_indexkey_for_join
- *	  Generates a list of clauses that can be used with an index
+ *	  Generate a list of sublists of clauses that can be used with an index
  *	  to scan the inner side of a nestloop join.
  *
  * This is much like group_clauses_by_indexkey(), but we consider both
@@ -652,23 +655,20 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 		if (clausegroup == NIL)
 			break;
 
-		clausegroup_list = nconc(clausegroup_list, clausegroup);
+		clausegroup_list = lappend(clausegroup_list, clausegroup);
 
 		indexkeys++;
 		classes++;
 
 	} while (!DoneMatchingIndexKeys(indexkeys, classes));
 
-	/*
-	 * if no join clause was matched then forget it, per comments above.
-	 */
+	/* if no join clause was matched then forget it, per comments above */
 	if (!jfound)
 	{
 		freeList(clausegroup_list);
 		return NIL;
 	}
 
-	/* clausegroup_list holds all matched clauses ordered by indexkeys */
 	return clausegroup_list;
 }
 
@@ -1124,8 +1124,6 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	ExprState  *test_exprstate;
 	Datum		test_result;
 	bool		isNull;
-	HeapTuple	test_tuple;
-	Form_pg_amop test_form;
 	CatCList   *catlist;
 	int			i;
 	EState	   *estate;
@@ -1241,22 +1239,13 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 	/*
 	 * 3. From the same opclass, find the operator for the test strategy
 	 */
-	test_tuple = SearchSysCache(AMOPSTRATEGY,
-								ObjectIdGetDatum(opclass_id),
-								Int16GetDatum(test_strategy),
-								0, 0);
-	if (!HeapTupleIsValid(test_tuple))
+	test_op = get_opclass_member(opclass_id, test_strategy);
+	if (!OidIsValid(test_op))
 	{
 		/* This should not fail, else pg_amop entry is missing */
 		elog(ERROR, "Missing pg_amop entry for opclass %u strategy %d",
 			 opclass_id, test_strategy);
 	}
-	test_form = (Form_pg_amop) GETSTRUCT(test_tuple);
-
-	/* Get the test operator */
-	test_op = test_form->amopopr;
-
-	ReleaseSysCache(test_tuple);
 
 	/*
 	 * 4. Evaluate the test.  For this we need an EState.
@@ -1488,22 +1477,18 @@ best_inner_indexscan(Query *root, RelOptInfo *rel,
 
 		if (jlist == NIL)		/* failed to find a match? */
 		{
-			List	   *clausegroup;
+			List	   *clausegroups;
 
 			/* find useful clauses for this index and outerjoin set */
-			clausegroup = group_clauses_by_indexkey_for_join(rel,
-															 index,
-															 index_outer_relids,
-															 isouterjoin);
-			if (clausegroup)
+			clausegroups = group_clauses_by_indexkey_for_join(rel,
+															  index,
+															  index_outer_relids,
+															  isouterjoin);
+			if (clausegroups)
 			{
-				/* remove duplicate and redundant clauses */
-				clausegroup = remove_redundant_join_clauses(root,
-															clausegroup,
-															jointype);
 				/* make the path */
 				path = make_innerjoin_index_path(root, rel, index,
-												 clausegroup);
+												 clausegroups);
 			}
 
 			/* Cache the result --- whether positive or negative */
@@ -1542,15 +1527,17 @@ best_inner_indexscan(Query *root, RelOptInfo *rel,
  *	  relation in a nestloop join.
  *
  * 'rel' is the relation for which 'index' is defined
- * 'clausegroup' is a list of restrictinfo nodes that can use 'index'
+ * 'clausegroups' is a list of lists of RestrictInfos that can use 'index'
  */
 static Path *
 make_innerjoin_index_path(Query *root,
 						  RelOptInfo *rel, IndexOptInfo *index,
-						  List *clausegroup)
+						  List *clausegroups)
 {
 	IndexPath  *pathnode = makeNode(IndexPath);
-	List	   *indexquals;
+	List	   *indexquals,
+			   *allclauses,
+			   *l;
 
 	/* XXX this code ought to be merged with create_index_path? */
 
@@ -1564,11 +1551,8 @@ make_innerjoin_index_path(Query *root,
 	 */
 	pathnode->path.pathkeys = NIL;
 
-	/* Extract bare indexqual clauses from restrictinfos */
-	indexquals = get_actual_clauses(clausegroup);
-
-	/* expand special operators to indexquals the executor can handle */
-	indexquals = expand_indexqual_conditions(indexquals);
+	/* Convert RestrictInfo nodes to indexquals the executor can handle */
+	indexquals = expand_indexqual_conditions(index, clausegroups);
 
 	/*
 	 * Note that we are making a pathnode for a single-scan indexscan;
@@ -1583,24 +1567,31 @@ make_innerjoin_index_path(Query *root,
 	/*
 	 * We must compute the estimated number of output rows for the
 	 * indexscan.  This is less than rel->rows because of the
-	 * additional selectivity of the join clauses.	Since clausegroup
+	 * additional selectivity of the join clauses.	Since clausegroups
 	 * may contain both restriction and join clauses, we have to do a
 	 * set union to get the full set of clauses that must be
-	 * considered to compute the correct selectivity.  (We can't just
-	 * nconc the two lists; then we might have some restriction
-	 * clauses appearing twice, which'd mislead
-	 * restrictlist_selectivity into double-counting their
+	 * considered to compute the correct selectivity.  (Without the union
+	 * operation, we might have some restriction clauses appearing twice,
+	 * which'd mislead restrictlist_selectivity into double-counting their
 	 * selectivity.  However, since RestrictInfo nodes aren't copied when
 	 * linking them into different lists, it should be sufficient to use
 	 * pointer comparison to remove duplicates.)
 	 *
+	 * We assume we can destructively modify the input sublists.
+	 *
 	 * Always assume the join type is JOIN_INNER; even if some of the
 	 * join clauses come from other contexts, that's not our problem.
 	 */
+	allclauses = NIL;
+	foreach(l, clausegroups)
+	{
+		/* nconc okay here since same clause couldn't be in two sublists */
+		allclauses = nconc(allclauses, (List *) lfirst(l));
+	}
+	allclauses = set_ptrUnion(rel->baserestrictinfo, allclauses);
 	pathnode->rows = rel->tuples *
 		restrictlist_selectivity(root,
-								 set_ptrUnion(rel->baserestrictinfo,
-											  clausegroup),
+								 allclauses,
 								 rel->relid,
 								 JOIN_INNER);
 	/* Like costsize.c, force estimate to be at least one row */
@@ -1741,10 +1732,10 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
  * 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,
+ * expand_indexqual_conditions() converts a list of lists of RestrictInfo
+ * nodes (with implicit AND semantics across list elements) into
+ * a list of clauses 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.
  *----------
@@ -1765,10 +1756,9 @@ match_special_index_operator(Expr *clause, Oid opclass,
 							 bool indexkey_on_left)
 {
 	bool		isIndexable = false;
-	Node	   *leftop,
-			   *rightop;
+	Node	   *rightop;
 	Oid			expr_op;
-	Const	   *patt = NULL;
+	Const	   *patt;
 	Const	   *prefix = NULL;
 	Const	   *rest = NULL;
 
@@ -1781,7 +1771,6 @@ match_special_index_operator(Expr *clause, Oid opclass,
 		return false;
 
 	/* we know these will succeed */
-	leftop = get_leftop(clause);
 	rightop = get_rightop(clause);
 	expr_op = ((OpExpr *) clause)->opno;
 
@@ -1795,7 +1784,6 @@ match_special_index_operator(Expr *clause, Oid opclass,
 	{
 		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 */
 			isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
@@ -1809,7 +1797,6 @@ match_special_index_operator(Expr *clause, Oid opclass,
 
 		case OID_TEXT_ICLIKE_OP:
 		case OID_BPCHAR_ICLIKE_OP:
-		case OID_VARCHAR_ICLIKE_OP:
 		case OID_NAME_ICLIKE_OP:
 			/* the right-hand const is type text for all of these */
 			isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
@@ -1818,7 +1805,6 @@ match_special_index_operator(Expr *clause, Oid opclass,
 
 		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 */
 			isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex,
@@ -1827,7 +1813,6 @@ match_special_index_operator(Expr *clause, Oid opclass,
 
 		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 */
 			isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
@@ -1855,8 +1840,11 @@ match_special_index_operator(Expr *clause, Oid opclass,
 	/*
 	 * Must also check that index's opclass supports the operators we will
 	 * want to apply.  (A hash index, for example, will not support ">=".)
-	 * We cheat a little by not checking for availability of "=" ... any
-	 * index type should support "=", methinks.
+	 * Currently, only btree supports the operators we need.
+	 *
+	 * We insist on the opclass being the specific one we expect,
+	 * else we'd do the wrong thing if someone were to make a reverse-sort
+	 * opclass with the same operators.
 	 */
 	switch (expr_op)
 	{
@@ -1864,69 +1852,44 @@ match_special_index_operator(Expr *clause, Oid opclass,
 		case OID_TEXT_ICLIKE_OP:
 		case OID_TEXT_REGEXEQ_OP:
 		case OID_TEXT_ICREGEXEQ_OP:
-			if (lc_collate_is_c())
-				isIndexable = (op_in_opclass(find_operator(">=", TEXTOID), opclass)
-							   && op_in_opclass(find_operator("<", TEXTOID), opclass));
-			else
-				isIndexable = (op_in_opclass(find_operator("~>=~", TEXTOID), opclass)
-							   && op_in_opclass(find_operator("~<~", TEXTOID), opclass));
-			break;
-
-		case OID_BYTEA_LIKE_OP:
-			isIndexable = (op_in_opclass(find_operator(">=", BYTEAOID), opclass)
-						   && op_in_opclass(find_operator("<", BYTEAOID), opclass));
+			/* text operators will be used for varchar inputs, too */
+			isIndexable =
+				(opclass == TEXT_PATTERN_BTREE_OPS_OID) ||
+				(opclass == TEXT_BTREE_OPS_OID && lc_collate_is_c()) ||
+				(opclass == VARCHAR_PATTERN_BTREE_OPS_OID) ||
+				(opclass == VARCHAR_BTREE_OPS_OID && lc_collate_is_c());
 			break;
 
 		case OID_BPCHAR_LIKE_OP:
 		case OID_BPCHAR_ICLIKE_OP:
 		case OID_BPCHAR_REGEXEQ_OP:
 		case OID_BPCHAR_ICREGEXEQ_OP:
-			if (lc_collate_is_c())
-				isIndexable = (op_in_opclass(find_operator(">=", BPCHAROID), opclass)
-							   && op_in_opclass(find_operator("<", BPCHAROID), opclass));
-			else
-				isIndexable = (op_in_opclass(find_operator("~>=~", BPCHAROID), opclass)
-							   && op_in_opclass(find_operator("~<~", BPCHAROID), opclass));
-			break;
-
-		case OID_VARCHAR_LIKE_OP:
-		case OID_VARCHAR_ICLIKE_OP:
-		case OID_VARCHAR_REGEXEQ_OP:
-		case OID_VARCHAR_ICREGEXEQ_OP:
-			if (lc_collate_is_c())
-				isIndexable = (op_in_opclass(find_operator(">=", VARCHAROID), opclass)
-							   && op_in_opclass(find_operator("<", VARCHAROID), opclass));
-			else
-				isIndexable = (op_in_opclass(find_operator("~>=~", VARCHAROID), opclass)
-							   && op_in_opclass(find_operator("~<~", VARCHAROID), opclass));
+			isIndexable =
+				(opclass == BPCHAR_PATTERN_BTREE_OPS_OID) ||
+				(opclass == BPCHAR_BTREE_OPS_OID && lc_collate_is_c());
 			break;
 
 		case OID_NAME_LIKE_OP:
 		case OID_NAME_ICLIKE_OP:
 		case OID_NAME_REGEXEQ_OP:
 		case OID_NAME_ICREGEXEQ_OP:
-			if (lc_collate_is_c())
-				isIndexable = (op_in_opclass(find_operator(">=", NAMEOID), opclass)
-							   && op_in_opclass(find_operator("<", NAMEOID), opclass));
-			else
-				isIndexable = (op_in_opclass(find_operator("~>=~", NAMEOID), opclass)
-							   && op_in_opclass(find_operator("~<~", NAMEOID), opclass));
+			isIndexable =
+				(opclass == NAME_PATTERN_BTREE_OPS_OID) ||
+				(opclass == NAME_BTREE_OPS_OID && lc_collate_is_c());
+			break;
+
+		case OID_BYTEA_LIKE_OP:
+			isIndexable = (opclass == BYTEA_BTREE_OPS_OID);
 			break;
 
 		case OID_INET_SUB_OP:
 		case OID_INET_SUBEQ_OP:
-			/* for SUB we actually need ">" not ">=", but this should do */
-			if (!op_in_opclass(find_operator(">=", INETOID), opclass) ||
-				!op_in_opclass(find_operator("<=", INETOID), opclass))
-				isIndexable = false;
+			isIndexable = (opclass == INET_BTREE_OPS_OID);
 			break;
 
 		case OID_CIDR_SUB_OP:
 		case OID_CIDR_SUBEQ_OP:
-			/* for SUB we actually need ">" not ">=", but this should do */
-			if (!op_in_opclass(find_operator(">=", CIDROID), opclass) ||
-				!op_in_opclass(find_operator("<=", CIDROID), opclass))
-				isIndexable = false;
+			isIndexable = (opclass == CIDR_BTREE_OPS_OID);
 			break;
 	}
 
@@ -1935,185 +1898,217 @@ match_special_index_operator(Expr *clause, Oid opclass,
 
 /*
  * 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.
+ *	  Given a list of sublists of RestrictInfo nodes, produce a flat list
+ *	  of index qual clauses.  Standard qual clauses (those in the index's
+ *	  opclass) are passed through unchanged.  "Special" index operators
+ *	  are expanded into clauses that the indexscan machinery will know
+ *	  what to do with.
+ *
+ * The input list is ordered by index key, and so the output list is too.
+ * (The latter is not depended on by any part of the planner, so far as I can
+ * tell; but some parts of the executor do assume that the indxqual list
+ * ultimately delivered to the executor is so ordered.  One such place is
+ * _bt_orderkeys() in the btree support.  Perhaps that ought to be fixed
+ * someday --- tgl 7/00)
  */
 List *
-expand_indexqual_conditions(List *indexquals)
+expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 {
 	List	   *resultquals = NIL;
-	List	   *q;
+	int		   *indexkeys = index->indexkeys;
+	Oid		   *classes = index->classlist;
+
+	if (clausegroups == NIL)
+		return NIL;
 
-	foreach(q, indexquals)
+	do
 	{
-		Expr	   *clause = (Expr *) lfirst(q);
-
-		/* we know these will succeed */
-		Node	   *leftop = get_leftop(clause);
-		Node	   *rightop = get_rightop(clause);
-		Oid			expr_op = ((OpExpr *) clause)->opno;
-		Const	   *patt = (Const *) rightop;
-		Const	   *prefix = NULL;
-		Const	   *rest = NULL;
-		Pattern_Prefix_Status pstatus;
-
-		switch (expr_op)
+		Oid			curClass = classes[0];
+		List	   *i;
+
+		foreach(i, (List *) lfirst(clausegroups))
 		{
-				/*
-				 * 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:
-			case OID_BYTEA_LIKE_OP:
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
-											   &prefix, &rest);
-				resultquals = nconc(resultquals,
-									prefix_quals(leftop, expr_op,
-												 prefix, pstatus));
-				break;
+			RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
 
-			case OID_TEXT_ICLIKE_OP:
-			case OID_BPCHAR_ICLIKE_OP:
-			case OID_VARCHAR_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,
-											   &prefix, &rest);
-				resultquals = nconc(resultquals,
-									prefix_quals(leftop, expr_op,
-												 prefix, pstatus));
-				break;
+			resultquals = nconc(resultquals,
+								expand_indexqual_condition(rinfo->clause,
+														   curClass));
+		}
 
-			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 */
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex,
-											   &prefix, &rest);
-				resultquals = nconc(resultquals,
-									prefix_quals(leftop, expr_op,
-												 prefix, pstatus));
-				break;
+		clausegroups = lnext(clausegroups);
 
-			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 */
-				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
-											   &prefix, &rest);
-				resultquals = nconc(resultquals,
-									prefix_quals(leftop, expr_op,
-												 prefix, pstatus));
-				break;
+		indexkeys++;
+		classes++;
 
-			case OID_INET_SUB_OP:
-			case OID_INET_SUBEQ_OP:
-			case OID_CIDR_SUB_OP:
-			case OID_CIDR_SUBEQ_OP:
-				resultquals = nconc(resultquals,
-									network_prefix_quals(leftop, expr_op,
-													  patt->constvalue));
-				break;
+	} while (clausegroups != NIL &&
+			 !DoneMatchingIndexKeys(indexkeys, classes));
 
-			default:
-				resultquals = lappend(resultquals, clause);
-				break;
-		}
-	}
+	Assert(clausegroups == NIL); /* else more groups than indexkeys... */
 
 	return resultquals;
 }
 
+/*
+ * expand_indexqual_condition --- expand a single indexqual condition
+ */
+static List *
+expand_indexqual_condition(Expr *clause, Oid opclass)
+{
+	/* we know these will succeed */
+	Node	   *leftop = get_leftop(clause);
+	Node	   *rightop = get_rightop(clause);
+	Oid			expr_op = ((OpExpr *) clause)->opno;
+	Const	   *patt = (Const *) rightop;
+	Const	   *prefix = NULL;
+	Const	   *rest = NULL;
+	Pattern_Prefix_Status pstatus;
+	List	   *result;
+
+	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_NAME_LIKE_OP:
+		case OID_BYTEA_LIKE_OP:
+			pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like,
+										   &prefix, &rest);
+			result = prefix_quals(leftop, opclass, prefix, pstatus);
+			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,
+										   &prefix, &rest);
+			result = prefix_quals(leftop, opclass, prefix, pstatus);
+			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,
+										   &prefix, &rest);
+			result = prefix_quals(leftop, opclass, prefix, pstatus);
+			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,
+										   &prefix, &rest);
+			result = prefix_quals(leftop, opclass, prefix, pstatus);
+			break;
+
+		case OID_INET_SUB_OP:
+		case OID_INET_SUBEQ_OP:
+		case OID_CIDR_SUB_OP:
+		case OID_CIDR_SUBEQ_OP:
+			result = network_prefix_quals(leftop, expr_op, opclass,
+										  patt->constvalue);
+			break;
+
+		default:
+			result = makeList1(clause);
+			break;
+	}
+
+	return result;
+}
+
 /*
  * 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
+ * generate suitable indexqual condition(s).  opclass is the index
+ * operator class; we use it to deduce the appropriate comparison
  * operators and operand datatypes.
  */
 static List *
-prefix_quals(Node *leftop, Oid expr_op,
+prefix_quals(Node *leftop, Oid opclass,
 			 Const *prefix_const, Pattern_Prefix_Status pstatus)
 {
 	List	   *result;
 	Oid			datatype;
 	Oid			oproid;
-	const char *oprname;
-	char	   *prefix;
-	Const	   *con;
 	Expr	   *expr;
-	Const	   *greaterstr = NULL;
+	Const	   *greaterstr;
 
 	Assert(pstatus != Pattern_Prefix_None);
 
-	switch (expr_op)
+	switch (opclass)
 	{
-		case OID_TEXT_LIKE_OP:
-		case OID_TEXT_ICLIKE_OP:
-		case OID_TEXT_REGEXEQ_OP:
-		case OID_TEXT_ICREGEXEQ_OP:
+		case TEXT_BTREE_OPS_OID:
+		case TEXT_PATTERN_BTREE_OPS_OID:
 			datatype = TEXTOID;
 			break;
 
-		case OID_BYTEA_LIKE_OP:
-			datatype = BYTEAOID;
+		case VARCHAR_BTREE_OPS_OID:
+		case VARCHAR_PATTERN_BTREE_OPS_OID:
+			datatype = VARCHAROID;
 			break;
 
-		case OID_BPCHAR_LIKE_OP:
-		case OID_BPCHAR_ICLIKE_OP:
-		case OID_BPCHAR_REGEXEQ_OP:
-		case OID_BPCHAR_ICREGEXEQ_OP:
+		case BPCHAR_BTREE_OPS_OID:
+		case BPCHAR_PATTERN_BTREE_OPS_OID:
 			datatype = BPCHAROID;
 			break;
 
-		case OID_VARCHAR_LIKE_OP:
-		case OID_VARCHAR_ICLIKE_OP:
-		case OID_VARCHAR_REGEXEQ_OP:
-		case OID_VARCHAR_ICREGEXEQ_OP:
-			datatype = VARCHAROID;
+		case NAME_BTREE_OPS_OID:
+		case NAME_PATTERN_BTREE_OPS_OID:
+			datatype = NAMEOID;
 			break;
 
-		case OID_NAME_LIKE_OP:
-		case OID_NAME_ICLIKE_OP:
-		case OID_NAME_REGEXEQ_OP:
-		case OID_NAME_ICREGEXEQ_OP:
-			datatype = NAMEOID;
+		case BYTEA_BTREE_OPS_OID:
+			datatype = BYTEAOID;
 			break;
 
 		default:
-			elog(ERROR, "prefix_quals: unexpected operator %u", expr_op);
+			elog(ERROR, "prefix_quals: unexpected opclass %u", opclass);
 			return NIL;
 	}
 
-	/* Prefix constant is text for all except BYTEA_LIKE */
-	if (datatype != BYTEAOID)
-		prefix = DatumGetCString(DirectFunctionCall1(textout,
-													 prefix_const->constvalue));
-	else
-		prefix = DatumGetCString(DirectFunctionCall1(byteaout,
-													 prefix_const->constvalue));
+	/*
+	 * If necessary, coerce the prefix constant to the right type.
+	 * The given prefix constant is either text or bytea type.
+	 */
+	if (prefix_const->consttype != datatype)
+	{
+		char   *prefix;
+
+		switch (prefix_const->consttype)
+		{
+			case TEXTOID:
+				prefix = DatumGetCString(DirectFunctionCall1(textout,
+															 prefix_const->constvalue));
+				break;
+			case BYTEAOID:
+				prefix = DatumGetCString(DirectFunctionCall1(byteaout,
+															 prefix_const->constvalue));
+				break;
+			default:
+				elog(ERROR, "prefix_quals: unexpected consttype %u",
+					 prefix_const->consttype);
+				return NIL;
+		}
+		prefix_const = string_to_const(prefix, datatype);
+		pfree(prefix);
+	}
 
 	/*
 	 * If we found an exact-match pattern, generate an "=" indexqual.
 	 */
 	if (pstatus == Pattern_Prefix_Exact)
 	{
-		oprname = (datatype == BYTEAOID || lc_collate_is_c() ? "=" : "~=~");
-		oproid = find_operator(oprname, datatype);
+		oproid = get_opclass_member(opclass, BTEqualStrategyNumber);
 		if (oproid == InvalidOid)
-			elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype);
-		con = string_to_const(prefix, datatype);
+			elog(ERROR, "prefix_quals: no operator = for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
-							 (Expr *) leftop, (Expr *) con);
+							 (Expr *) leftop, (Expr *) prefix_const);
 		result = makeList1(expr);
 		return result;
 	}
@@ -2123,13 +2118,11 @@ prefix_quals(Node *leftop, Oid expr_op,
 	 *
 	 * We can always say "x >= prefix".
 	 */
-	oprname = (datatype == BYTEAOID || lc_collate_is_c() ? ">=" : "~>=~");
-	oproid = find_operator(oprname, datatype);
+	oproid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
 	if (oproid == InvalidOid)
-		elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype);
-	con = string_to_const(prefix, datatype);
+		elog(ERROR, "prefix_quals: no operator >= for opclass %u", opclass);
 	expr = make_opclause(oproid, BOOLOID, false,
-						 (Expr *) leftop, (Expr *) con);
+						 (Expr *) leftop, (Expr *) prefix_const);
 	result = makeList1(expr);
 
 	/*-------
@@ -2137,13 +2130,12 @@ prefix_quals(Node *leftop, Oid expr_op,
 	 * "x < greaterstr".
 	 *-------
 	 */
-	greaterstr = make_greater_string(con);
+	greaterstr = make_greater_string(prefix_const);
 	if (greaterstr)
 	{
-		oprname = (datatype == BYTEAOID || lc_collate_is_c() ? "<" : "~<~");
-		oproid = find_operator(oprname, datatype);
+		oproid = get_opclass_member(opclass, BTLessStrategyNumber);
 		if (oproid == InvalidOid)
-			elog(ERROR, "prefix_quals: no operator %s for type %u", oprname, datatype);
+			elog(ERROR, "prefix_quals: no operator < for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
 							 (Expr *) leftop, (Expr *) greaterstr);
 		result = lappend(result, expr);
@@ -2155,19 +2147,18 @@ prefix_quals(Node *leftop, Oid expr_op,
 /*
  * Given a leftop and a rightop, and a inet-class sup/sub operator,
  * generate suitable indexqual condition(s).  expr_op is the original
- * operator.
+ * operator, and opclass is the index opclass.
  */
 static List *
-network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop)
+network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
 {
 	bool		is_eq;
-	char	   *opr1name;
-	Datum		opr1right;
-	Datum		opr2right;
+	Oid			datatype;
 	Oid			opr1oid;
 	Oid			opr2oid;
+	Datum		opr1right;
+	Datum		opr2right;
 	List	   *result;
-	Oid			datatype;
 	Expr	   *expr;
 
 	switch (expr_op)
@@ -2198,12 +2189,20 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop)
 	 * create clause "key >= network_scan_first( rightop )", or ">" if the
 	 * operator disallows equality.
 	 */
-
-	opr1name = is_eq ? ">=" : ">";
-	opr1oid = find_operator(opr1name, datatype);
-	if (opr1oid == InvalidOid)
-		elog(ERROR, "network_prefix_quals: no %s operator for type %u",
-			 opr1name, datatype);
+	if (is_eq)
+	{
+		opr1oid = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
+		if (opr1oid == InvalidOid)
+			elog(ERROR, "network_prefix_quals: no >= operator for opclass %u",
+				 opclass);
+	}
+	else
+	{
+		opr1oid = get_opclass_member(opclass, BTGreaterStrategyNumber);
+		if (opr1oid == InvalidOid)
+			elog(ERROR, "network_prefix_quals: no > operator for opclass %u",
+				 opclass);
+	}
 
 	opr1right = network_scan_first(rightop);
 
@@ -2215,10 +2214,10 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop)
 
 	/* create clause "key <= network_scan_last( rightop )" */
 
-	opr2oid = find_operator("<=", datatype);
+	opr2oid = get_opclass_member(opclass, BTLessEqualStrategyNumber);
 	if (opr2oid == InvalidOid)
-		elog(ERROR, "network_prefix_quals: no <= operator for type %u",
-			 datatype);
+		elog(ERROR, "network_prefix_quals: no <= operator for opclass %u",
+			 opclass);
 
 	opr2right = network_scan_last(rightop);
 
@@ -2235,18 +2234,6 @@ network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop)
  * Handy subroutines for match_special_index_operator() and friends.
  */
 
-/* See if there is a binary op of the given name for the given datatype */
-/* NB: we assume that only built-in system operators are searched for */
-static Oid
-find_operator(const char *opname, Oid datatype)
-{
-	return GetSysCacheOid(OPERNAMENSP,
-						  PointerGetDatum(opname),
-						  ObjectIdGetDatum(datatype),
-						  ObjectIdGetDatum(datatype),
-						  ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
-}
-
 /*
  * Generate a Datum of the appropriate type from a C string.
  * Note that all of the supported types are pass-by-ref, so the
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 3984c666f51242e2a6c6a3fe932c35fbe7db2c9a..25648beed19dd465be50a76610ffe3e5e02bd070 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.88 2003/02/15 20:12:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.89 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -328,7 +328,7 @@ create_seqscan_path(Query *root, RelOptInfo *rel)
  *
  * 'rel' is the parent rel
  * 'index' is an index on 'rel'
- * 'restriction_clauses' is a list of RestrictInfo nodes
+ * 'restriction_clauses' is a list of lists of RestrictInfo nodes
  *			to be used as index qual conditions in the scan.
  * 'pathkeys' describes the ordering of the path.
  * 'indexscandir' is ForwardScanDirection or BackwardScanDirection
@@ -352,9 +352,8 @@ create_index_path(Query *root,
 	pathnode->path.parent = rel;
 	pathnode->path.pathkeys = pathkeys;
 
-	indexquals = get_actual_clauses(restriction_clauses);
-	/* expand special operators to indexquals the executor can handle */
-	indexquals = expand_indexqual_conditions(indexquals);
+	/* Convert RestrictInfo nodes to indexquals the executor can handle */
+	indexquals = expand_indexqual_conditions(index, restriction_clauses);
 
 	/*
 	 * We are making a pathnode for a single-scan indexscan; therefore,
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 9dc0c7f1c19ba4f0b66a952676e09ed567dd0dde..fc35c2d6d41579171680dd0bbad6f62749154662 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.96 2003/04/29 22:13:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,7 +32,6 @@
 static Node *coerce_type_typmod(Node *node,
 								Oid targetTypeId, int32 targetTypMod,
 								CoercionForm cformat, bool isExplicit);
-static Oid	PreferredType(CATEGORY category, Oid type);
 static Node *build_func_call(Oid funcid, Oid rettype, List *args,
 							 CoercionForm fformat);
 
@@ -66,28 +65,43 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 	if (can_coerce_type(1, &exprtype, &targettype, ccontext))
 		expr = coerce_type(pstate, expr, exprtype, targettype,
 						   ccontext, cformat);
-	/*
-	 * String hacks to get transparent conversions for char and varchar:
-	 * if a coercion to text is available, use it for forced coercions to
-	 * char(n) or varchar(n).
-	 *
-	 * This is pretty grotty, but seems easier to maintain than providing
-	 * entries in pg_cast that parallel all the ones for text.
-	 */
-	else if (ccontext >= COERCION_ASSIGNMENT &&
-			 (targettype == BPCHAROID || targettype == VARCHAROID))
+	else if (ccontext >= COERCION_ASSIGNMENT)
 	{
-		Oid			text_id = TEXTOID;
+		/*
+		 * String hacks to get transparent conversions for char and varchar:
+		 * if a coercion to text is available, use it for forced coercions to
+		 * char(n) or varchar(n) or domains thereof.
+		 *
+		 * This is pretty grotty, but seems easier to maintain than providing
+		 * entries in pg_cast that parallel all the ones for text.
+		 */
+		Oid		targetbasetype = getBaseType(targettype);
 
-		if (can_coerce_type(1, &exprtype, &text_id, ccontext))
+		if (targetbasetype == BPCHAROID || targetbasetype == VARCHAROID)
 		{
-			expr = coerce_type(pstate, expr, exprtype, text_id,
-							   ccontext, cformat);
-			/* Need a RelabelType if no typmod coercion is performed */
-			if (targettypmod < 0)
-				expr = (Node *) makeRelabelType((Expr *) expr,
-												targettype, -1,
-												cformat);
+			Oid			text_id = TEXTOID;
+
+			if (can_coerce_type(1, &exprtype, &text_id, ccontext))
+			{
+				expr = coerce_type(pstate, expr, exprtype, text_id,
+								   ccontext, cformat);
+				if (targetbasetype != targettype)
+				{
+					/* need to coerce to domain over char or varchar */
+					expr = coerce_to_domain(expr, targetbasetype, targettype,
+											cformat);
+				}
+				else
+				{
+					/* need a RelabelType if no typmod coercion will be performed */
+					if (targettypmod < 0)
+						expr = (Node *) makeRelabelType((Expr *) expr,
+														targettype, -1,
+														cformat);
+				}
+			}
+			else
+				expr = NULL;
 		}
 		else
 			expr = NULL;
@@ -923,7 +937,10 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 
 
 /* TypeCategory()
- * Assign a category to the specified OID.
+ *		Assign a category to the specified type OID.
+ *
+ * NB: this must not return INVALID_TYPE.
+ *
  * XXX This should be moved to system catalog lookups
  * to allow for better type extensibility.
  * - thomas 2001-09-30
@@ -1026,7 +1043,11 @@ TypeCategory(Oid inType)
 
 
 /* IsPreferredType()
- * Check if this type is a preferred type.
+ *		Check if this type is a preferred type for the given category.
+ *
+ * If category is INVALID_TYPE, then we'll return TRUE for preferred types
+ * of any category; otherwise, only for preferred types of that category.
+ *
  * XXX This should be moved to system catalog lookups
  * to allow for better type extensibility.
  * - thomas 2001-09-30
@@ -1034,39 +1055,34 @@ TypeCategory(Oid inType)
 bool
 IsPreferredType(CATEGORY category, Oid type)
 {
-	return (type == PreferredType(category, type));
-}	/* IsPreferredType() */
+	Oid			preftype;
 
+	if (category == INVALID_TYPE)
+		category = TypeCategory(type);
+	else if (category != TypeCategory(type))
+		return false;
 
-/* PreferredType()
- * Return the preferred type OID for the specified category.
- * XXX This should be moved to system catalog lookups
- * to allow for better type extensibility.
- * - thomas 2001-09-30
- */
-static Oid
-PreferredType(CATEGORY category, Oid type)
-{
-	Oid			result;
-
+	/*
+	 * This switch should agree with TypeCategory(), above.  Note that
+	 * at this point, category certainly matches the type.
+	 */
 	switch (category)
 	{
-		case (INVALID_TYPE):
 		case (UNKNOWN_TYPE):
 		case (GENERIC_TYPE):
-			result = UNKNOWNOID;
+			preftype = UNKNOWNOID;
 			break;
 
 		case (BOOLEAN_TYPE):
-			result = BOOLOID;
+			preftype = BOOLOID;
 			break;
 
 		case (STRING_TYPE):
-			result = TEXTOID;
+			preftype = TEXTOID;
 			break;
 
 		case (BITSTRING_TYPE):
-			result = VARBITOID;
+			preftype = VARBITOID;
 			break;
 
 		case (NUMERIC_TYPE):
@@ -1077,52 +1093,59 @@ PreferredType(CATEGORY category, Oid type)
 				type == REGOPERATOROID ||
 				type == REGCLASSOID ||
 				type == REGTYPEOID)
-				result = OIDOID;
+				preftype = OIDOID;
 			else
-				result = FLOAT8OID;
+				preftype = FLOAT8OID;
 			break;
 
 		case (DATETIME_TYPE):
 			if (type == DATEOID)
-				result = TIMESTAMPOID;
+				preftype = TIMESTAMPOID;
 			else
-				result = TIMESTAMPTZOID;
+				preftype = TIMESTAMPTZOID;
 			break;
 
 		case (TIMESPAN_TYPE):
-			result = INTERVALOID;
+			preftype = INTERVALOID;
 			break;
 
 		case (GEOMETRIC_TYPE):
-			result = type;
+			preftype = type;
 			break;
 
 		case (NETWORK_TYPE):
-			result = INETOID;
+			preftype = INETOID;
 			break;
 
 		case (USER_TYPE):
-			result = type;
+			preftype = type;
 			break;
 
 		default:
-			elog(ERROR, "PreferredType: unknown category");
-			result = UNKNOWNOID;
+			elog(ERROR, "IsPreferredType: unknown category");
+			preftype = UNKNOWNOID;
 			break;
 	}
-	return result;
-}	/* PreferredType() */
+
+	return (type == preftype);
+}	/* IsPreferredType() */
 
 
 /* IsBinaryCoercible()
  *		Check if srctype is binary-coercible to targettype.
  *
  * This notion allows us to cheat and directly exchange values without
- * going through the trouble of calling a conversion function.
+ * going through the trouble of calling a conversion function.  Note that
+ * in general, this should only be an implementation shortcut.  Before 7.4,
+ * this was also used as a heuristic for resolving overloaded functions and
+ * operators, but that's basically a bad idea.
  *
  * As of 7.3, binary coercibility isn't hardwired into the code anymore.
  * We consider two types binary-coercible if there is an implicitly
- * invokable, no-function-needed pg_cast entry.
+ * invokable, no-function-needed pg_cast entry.  Also, a domain is always
+ * binary-coercible to its base type, though *not* vice versa (in the other
+ * direction, one must apply domain constraint checks before accepting the
+ * value as legitimate).
  *
  * This function replaces IsBinaryCompatible(), which was an inherently
  * symmetric test.  Since the pg_cast entries aren't necessarily symmetric,
@@ -1139,13 +1162,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
 	if (srctype == targettype)
 		return true;
 
-	/* Perhaps the types are domains; if so, look at their base types */
+	/* If srctype is a domain, reduce to its base type */
 	if (OidIsValid(srctype))
 		srctype = getBaseType(srctype);
-	if (OidIsValid(targettype))
-		targettype = getBaseType(targettype);
 
-	/* Somewhat-fast path if same base type */
+	/* Somewhat-fast path for domain -> base type case */
 	if (srctype == targettype)
 		return true;
 
@@ -1174,8 +1195,13 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
  * ccontext determines the set of available casts.
  *
  * If we find a suitable entry in pg_cast, return TRUE, and set *funcid
- * to the castfunc value (which may be InvalidOid for a binary-compatible
- * coercion).
+ * to the castfunc value, which may be InvalidOid for a binary-compatible
+ * coercion.
+ *
+ * NOTE: *funcid == InvalidOid does not necessarily mean that no work is
+ * needed to do the coercion; if the target is a domain then we may need to
+ * apply domain constraint checking.  If you want to check for a zero-effort
+ * conversion then use IsBinaryCoercible().
  */
 bool
 find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
@@ -1193,7 +1219,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 	if (OidIsValid(targetTypeId))
 		targetTypeId = getBaseType(targetTypeId);
 
-	/* Domains are automatically binary-compatible with their base type */
+	/* Domains are always coercible to and from their base type */
 	if (sourceTypeId == targetTypeId)
 		return true;
 
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 058b9aad73dc8524b162955dfcf601a3b8031ddb..e9a40e03179b31c258918c6ddcce7dee3fcac3b6 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.147 2003/04/29 22:13:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.148 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,6 @@
 
 #include "access/heapam.h"
 #include "catalog/catname.h"
-#include "catalog/namespace.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_proc.h"
 #include "lib/stringinfo.h"
@@ -37,13 +36,7 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes);
 
 static int	find_inheritors(Oid relid, Oid **supervec);
 static Oid **gen_cross_product(InhPaths *arginh, int nargs);
-static int match_argtypes(int nargs,
-			   Oid *input_typeids,
-			   FuncCandidateList function_typeids,
-			   FuncCandidateList *candidates);
 static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
-static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids,
-					  FuncCandidateList candidates);
 static void unknown_attribute(const char *schemaname, const char *relname,
 				  const char *attname);
 
@@ -355,21 +348,24 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 }
 
 
-/* match_argtypes()
+/* func_match_argtypes()
  *
- * Given a list of possible typeid arrays to a function and an array of
- * input typeids, produce a shortlist of those function typeid arrays
- * that match the input typeids (either exactly or by coercion), and
- * return the number of such arrays.
+ * Given a list of candidate functions (having the right name and number
+ * of arguments) and an array of input datatype OIDs, produce a shortlist of
+ * those candidates that actually accept the input datatypes (either exactly
+ * or by coercion), and return the number of such candidates.
+ *
+ * Note that can_coerce_type will assume that UNKNOWN inputs are coercible to
+ * anything, so candidates will not be eliminated on that basis.
  *
  * NB: okay to modify input list structure, as long as we find at least
- * one match.
+ * one match.  If no match at all, the list must remain unmodified.
  */
-static int
-match_argtypes(int nargs,
-			   Oid *input_typeids,
-			   FuncCandidateList function_typeids,
-			   FuncCandidateList *candidates)	/* return value */
+int
+func_match_argtypes(int nargs,
+					Oid *input_typeids,
+					FuncCandidateList raw_candidates,
+					FuncCandidateList *candidates)	/* return value */
 {
 	FuncCandidateList current_candidate;
 	FuncCandidateList next_candidate;
@@ -377,7 +373,7 @@ match_argtypes(int nargs,
 
 	*candidates = NULL;
 
-	for (current_candidate = function_typeids;
+	for (current_candidate = raw_candidates;
 		 current_candidate != NULL;
 		 current_candidate = next_candidate)
 	{
@@ -392,21 +388,65 @@ match_argtypes(int nargs,
 	}
 
 	return ncandidates;
-}	/* match_argtypes() */
+}	/* func_match_argtypes() */
 
 
 /* func_select_candidate()
- * Given the input argtype array and more than one candidate
- * for the function, attempt to resolve the conflict.
+ *		Given the input argtype array and more than one candidate
+ *		for the function, attempt to resolve the conflict.
+ *
  * Returns the selected candidate if the conflict can be resolved,
  * otherwise returns NULL.
  *
- * By design, this is pretty similar to oper_select_candidate in parse_oper.c.
- * However, the calling convention is a little different: we assume the caller
- * already pruned away "candidates" that aren't actually coercion-compatible
- * with the input types, whereas oper_select_candidate must do that itself.
+ * Note that the caller has already determined that there is no candidate
+ * exactly matching the input argtypes, and has pruned away any "candidates"
+ * that aren't actually coercion-compatible with the input types.
+ *
+ * This is also used for resolving ambiguous operator references.  Formerly
+ * parse_oper.c had its own, essentially duplicate code for the purpose.
+ * The following comments (formerly in parse_oper.c) are kept to record some
+ * of the history of these heuristics.
+ *
+ * OLD COMMENTS:
+ *
+ * This routine is new code, replacing binary_oper_select_candidate()
+ * which dates from v4.2/v1.0.x days. It tries very hard to match up
+ * operators with types, including allowing type coercions if necessary.
+ * The important thing is that the code do as much as possible,
+ * while _never_ doing the wrong thing, where "the wrong thing" would
+ * be returning an operator when other better choices are available,
+ * or returning an operator which is a non-intuitive possibility.
+ * - thomas 1998-05-21
+ *
+ * The comments below came from binary_oper_select_candidate(), and
+ * illustrate the issues and choices which are possible:
+ * - thomas 1998-05-20
+ *
+ * current wisdom holds that the default operator should be one in which
+ * both operands have the same type (there will only be one such
+ * operator)
+ *
+ * 7.27.93 - I have decided not to do this; it's too hard to justify, and
+ * it's easy enough to typecast explicitly - avi
+ * [the rest of this routine was commented out since then - ay]
+ *
+ * 6/23/95 - I don't complete agree with avi. In particular, casting
+ * floats is a pain for users. Whatever the rationale behind not doing
+ * this is, I need the following special case to work.
+ *
+ * In the WHERE clause of a query, if a float is specified without
+ * quotes, we treat it as float8. I added the float48* operators so
+ * that we can operate on float4 and float8. But now we have more than
+ * one matching operator if the right arg is unknown (eg. float
+ * specified with quotes). This break some stuff in the regression
+ * test where there are floats in quotes not properly casted. Below is
+ * the solution. In addition to requiring the operator operates on the
+ * same type for both operands [as in the code Avi originally
+ * commented out], we also require that the operators be equivalent in
+ * some sense. (see equivalentOpersAfterPromotion for details.)
+ * - ay 6/95
  */
-static FuncCandidateList
+FuncCandidateList
 func_select_candidate(int nargs,
 					  Oid *input_typeids,
 					  FuncCandidateList candidates)
@@ -419,59 +459,28 @@ func_select_candidate(int nargs,
 	int			ncandidates;
 	int			nbestMatch,
 				nmatch;
+	Oid			input_base_typeids[FUNC_MAX_ARGS];
 	CATEGORY	slot_category[FUNC_MAX_ARGS],
 				current_category;
 	bool		slot_has_preferred_type[FUNC_MAX_ARGS];
 	bool		resolved_unknowns;
 
 	/*
-	 * Run through all candidates and keep those with the most matches on
-	 * exact types. Keep all candidates if none match.
+	 * If any input types are domains, reduce them to their base types.
+	 * This ensures that we will consider functions on the base type to be
+	 * "exact matches" in the exact-match heuristic; it also makes it possible
+	 * to do something useful with the type-category heuristics.  Note that
+	 * this makes it difficult, but not impossible, to use functions declared
+	 * to take a domain as an input datatype.  Such a function will be
+	 * selected over the base-type function only if it is an exact match at
+	 * all argument positions, and so was already chosen by our caller.
 	 */
-	ncandidates = 0;
-	nbestMatch = 0;
-	last_candidate = NULL;
-	for (current_candidate = candidates;
-		 current_candidate != NULL;
-		 current_candidate = current_candidate->next)
-	{
-		current_typeids = current_candidate->args;
-		nmatch = 0;
-		for (i = 0; i < nargs; i++)
-		{
-			if (input_typeids[i] != UNKNOWNOID &&
-				current_typeids[i] == input_typeids[i])
-				nmatch++;
-		}
-
-		/* take this one as the best choice so far? */
-		if ((nmatch > nbestMatch) || (last_candidate == NULL))
-		{
-			nbestMatch = nmatch;
-			candidates = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates = 1;
-		}
-		/* no worse than the last choice, so keep this one too? */
-		else if (nmatch == nbestMatch)
-		{
-			last_candidate->next = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates++;
-		}
-		/* otherwise, don't bother keeping this one... */
-	}
-
-	if (last_candidate)			/* terminate rebuilt list */
-		last_candidate->next = NULL;
-
-	if (ncandidates == 1)
-		return candidates;
+	for (i = 0; i < nargs; i++)
+		input_base_typeids[i] = getBaseType(input_typeids[i]);
 
 	/*
-	 * Still too many candidates? Run through all candidates and keep
-	 * those with the most matches on exact types + binary-compatible
-	 * types. Keep all candidates if none match.
+	 * Run through all candidates and keep those with the most matches on
+	 * exact types. Keep all candidates if none match.
 	 */
 	ncandidates = 0;
 	nbestMatch = 0;
@@ -484,11 +493,9 @@ func_select_candidate(int nargs,
 		nmatch = 0;
 		for (i = 0; i < nargs; i++)
 		{
-			if (input_typeids[i] != UNKNOWNOID)
-			{
-				if (IsBinaryCoercible(input_typeids[i], current_typeids[i]))
-					nmatch++;
-			}
+			if (input_base_typeids[i] != UNKNOWNOID &&
+				current_typeids[i] == input_base_typeids[i])
+				nmatch++;
 		}
 
 		/* take this one as the best choice so far? */
@@ -516,10 +523,14 @@ func_select_candidate(int nargs,
 		return candidates;
 
 	/*
-	 * Still too many candidates? Now look for candidates which are
-	 * preferred types at the args that will require coercion. Keep all
-	 * candidates if none match.
+	 * Still too many candidates? Now look for candidates which have either
+	 * exact matches or preferred types at the args that will require coercion.
+	 * (Restriction added in 7.4: preferred type must be of same category as
+	 * input type; give no preference to cross-category conversions to
+	 * preferred types.)  Keep all candidates if none match.
 	 */
+	for (i = 0; i < nargs; i++)			/* avoid multiple lookups */
+		slot_category[i] = TypeCategory(input_base_typeids[i]);
 	ncandidates = 0;
 	nbestMatch = 0;
 	last_candidate = NULL;
@@ -531,11 +542,10 @@ func_select_candidate(int nargs,
 		nmatch = 0;
 		for (i = 0; i < nargs; i++)
 		{
-			if (input_typeids[i] != UNKNOWNOID)
+			if (input_base_typeids[i] != UNKNOWNOID)
 			{
-				current_category = TypeCategory(current_typeids[i]);
-				if (current_typeids[i] == input_typeids[i] ||
-					IsPreferredType(current_category, current_typeids[i]))
+				if (current_typeids[i] == input_base_typeids[i] ||
+					IsPreferredType(slot_category[i], current_typeids[i]))
 					nmatch++;
 			}
 		}
@@ -565,6 +575,11 @@ func_select_candidate(int nargs,
 	 * Still too many candidates? Try assigning types for the unknown
 	 * columns.
 	 *
+	 * NOTE: for a binary operator with one unknown and one non-unknown input,
+	 * we already tried the heuristic of looking for a candidate with the
+	 * known input type on both sides (see binary_oper_exact()).  That's
+	 * essentially a special case of the general algorithm we try next.
+	 *
 	 * We do this by examining each unknown argument position to see if we
 	 * can determine a "type category" for it.	If any candidate has an
 	 * input datatype of STRING category, use STRING category (this bias
@@ -588,7 +603,7 @@ func_select_candidate(int nargs,
 	{
 		bool		have_conflict;
 
-		if (input_typeids[i] != UNKNOWNOID)
+		if (input_base_typeids[i] != UNKNOWNOID)
 			continue;
 		resolved_unknowns = true;		/* assume we can do it */
 		slot_category[i] = INVALID_TYPE;
@@ -656,7 +671,7 @@ func_select_candidate(int nargs,
 			current_typeids = current_candidate->args;
 			for (i = 0; i < nargs; i++)
 			{
-				if (input_typeids[i] != UNKNOWNOID)
+				if (input_base_typeids[i] != UNKNOWNOID)
 					continue;
 				current_type = current_typeids[i];
 				current_category = TypeCategory(current_type);
@@ -694,7 +709,7 @@ func_select_candidate(int nargs,
 	if (ncandidates == 1)
 		return candidates;
 
-	return NULL;				/* failed to determine a unique candidate */
+	return NULL;				/* failed to select a best candidate */
 }	/* func_select_candidate() */
 
 
@@ -734,16 +749,17 @@ func_get_detail(List *funcname,
 				bool *retset,	/* return value */
 				Oid **true_typeids)		/* return value */
 {
-	FuncCandidateList function_typeids;
+	FuncCandidateList raw_candidates;
 	FuncCandidateList best_candidate;
 
 	/* Get list of possible candidates from namespace search */
-	function_typeids = FuncnameGetCandidates(funcname, nargs);
+	raw_candidates = FuncnameGetCandidates(funcname, nargs);
 
 	/*
-	 * See if there is an exact match
+	 * Quickly check if there is an exact match to the input datatypes
+	 * (there can be only one)
 	 */
-	for (best_candidate = function_typeids;
+	for (best_candidate = raw_candidates;
 		 best_candidate != NULL;
 		 best_candidate = best_candidate->next)
 	{
@@ -815,7 +831,7 @@ func_get_detail(List *funcname,
 		 * didn't find an exact match, so now try to match up
 		 * candidates...
 		 */
-		if (function_typeids != NULL)
+		if (raw_candidates != NULL)
 		{
 			Oid		  **input_typeid_vector = NULL;
 			Oid		   *current_input_typeids;
@@ -829,17 +845,18 @@ func_get_detail(List *funcname,
 
 			do
 			{
-				FuncCandidateList current_function_typeids;
+				FuncCandidateList current_candidates;
 				int			ncandidates;
 
-				ncandidates = match_argtypes(nargs, current_input_typeids,
-											 function_typeids,
-											 &current_function_typeids);
+				ncandidates = func_match_argtypes(nargs,
+												  current_input_typeids,
+												  raw_candidates,
+												  &current_candidates);
 
 				/* one match only? then run with it... */
 				if (ncandidates == 1)
 				{
-					best_candidate = current_function_typeids;
+					best_candidate = current_candidates;
 					break;
 				}
 
@@ -851,7 +868,7 @@ func_get_detail(List *funcname,
 				{
 					best_candidate = func_select_candidate(nargs,
 												   current_input_typeids,
-											   current_function_typeids);
+											   current_candidates);
 
 					/*
 					 * If we were able to choose a best candidate, we're
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 6238258ed2f6448eea2e8dfa84e32b205460d541..21f73994c42b19965f3e959c70e989d65559a8fa 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,18 +8,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.63 2003/04/29 22:13:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/indexing.h"
-#include "catalog/namespace.h"
 #include "catalog/pg_operator.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
@@ -289,107 +284,29 @@ binary_oper_exact(Oid arg1, Oid arg2,
 
 
 /* oper_select_candidate()
- * Given the input argtype array and one or more candidates
- * for the function argtype array, attempt to resolve the conflict.
- * Returns the selected argtype array if the conflict can be resolved,
- * otherwise returns NULL.
+ *		Given the input argtype array and one or more candidates
+ *		for the operator, attempt to resolve the conflict.
  *
- * By design, this is pretty similar to func_select_candidate in parse_func.c.
- * However, we can do a couple of extra things here because we know we can
- * have no more than two args to deal with.  Also, the calling convention
- * is a little different: we must prune away "candidates" that aren't actually
- * coercion-compatible with the input types, whereas in parse_func.c that
- * gets done by match_argtypes before func_select_candidate is called.
+ * Returns the OID of the selected operator if the conflict can be resolved,
+ * otherwise returns InvalidOid.
  *
- * This routine is new code, replacing binary_oper_select_candidate()
- * which dates from v4.2/v1.0.x days. It tries very hard to match up
- * operators with types, including allowing type coercions if necessary.
- * The important thing is that the code do as much as possible,
- * while _never_ doing the wrong thing, where "the wrong thing" would
- * be returning an operator when other better choices are available,
- * or returning an operator which is a non-intuitive possibility.
- * - thomas 1998-05-21
- *
- * The comments below came from binary_oper_select_candidate(), and
- * illustrate the issues and choices which are possible:
- * - thomas 1998-05-20
- *
- * current wisdom holds that the default operator should be one in which
- * both operands have the same type (there will only be one such
- * operator)
- *
- * 7.27.93 - I have decided not to do this; it's too hard to justify, and
- * it's easy enough to typecast explicitly - avi
- * [the rest of this routine was commented out since then - ay]
- *
- * 6/23/95 - I don't complete agree with avi. In particular, casting
- * floats is a pain for users. Whatever the rationale behind not doing
- * this is, I need the following special case to work.
- *
- * In the WHERE clause of a query, if a float is specified without
- * quotes, we treat it as float8. I added the float48* operators so
- * that we can operate on float4 and float8. But now we have more than
- * one matching operator if the right arg is unknown (eg. float
- * specified with quotes). This break some stuff in the regression
- * test where there are floats in quotes not properly casted. Below is
- * the solution. In addition to requiring the operator operates on the
- * same type for both operands [as in the code Avi originally
- * commented out], we also require that the operators be equivalent in
- * some sense. (see equivalentOpersAfterPromotion for details.)
- * - ay 6/95
+ * Note that the caller has already determined that there is no candidate
+ * exactly matching the input argtype(s).  Incompatible candidates are not yet
+ * pruned away, however.
  */
 static Oid
 oper_select_candidate(int nargs,
 					  Oid *input_typeids,
 					  FuncCandidateList candidates)
 {
-	FuncCandidateList current_candidate;
-	FuncCandidateList last_candidate;
-	Oid		   *current_typeids;
-	Oid			current_type;
-	int			unknownOids;
-	int			i;
 	int			ncandidates;
-	int			nbestMatch,
-				nmatch;
-	CATEGORY	slot_category[FUNC_MAX_ARGS],
-				current_category;
-	bool		slot_has_preferred_type[FUNC_MAX_ARGS];
-	bool		resolved_unknowns;
 
 	/*
-	 * First, delete any candidates that cannot actually accept the given
-	 * input types, whether directly or by coercion.  (Note that
-	 * can_coerce_type will assume that UNKNOWN inputs are coercible to
-	 * anything, so candidates will not be eliminated on that basis.)
+	 * Delete any candidates that cannot actually accept the given
+	 * input types, whether directly or by coercion.
 	 */
-	ncandidates = 0;
-	last_candidate = NULL;
-	for (current_candidate = candidates;
-		 current_candidate != NULL;
-		 current_candidate = current_candidate->next)
-	{
-		if (can_coerce_type(nargs, input_typeids, current_candidate->args,
-							COERCION_IMPLICIT))
-		{
-			if (last_candidate == NULL)
-			{
-				candidates = current_candidate;
-				last_candidate = current_candidate;
-				ncandidates = 1;
-			}
-			else
-			{
-				last_candidate->next = current_candidate;
-				last_candidate = current_candidate;
-				ncandidates++;
-			}
-		}
-		/* otherwise, don't bother keeping this one... */
-	}
-
-	if (last_candidate)			/* terminate rebuilt list */
-		last_candidate->next = NULL;
+	ncandidates = func_match_argtypes(nargs, input_typeids,
+									  candidates, &candidates);
 
 	/* Done if no candidate or only one candidate survives */
 	if (ncandidates == 0)
@@ -398,317 +315,15 @@ oper_select_candidate(int nargs,
 		return candidates->oid;
 
 	/*
-	 * Run through all candidates and keep those with the most matches on
-	 * exact types. Keep all candidates if none match.
-	 */
-	ncandidates = 0;
-	nbestMatch = 0;
-	last_candidate = NULL;
-	for (current_candidate = candidates;
-		 current_candidate != NULL;
-		 current_candidate = current_candidate->next)
-	{
-		current_typeids = current_candidate->args;
-		nmatch = 0;
-		for (i = 0; i < nargs; i++)
-		{
-			if (input_typeids[i] != UNKNOWNOID &&
-				current_typeids[i] == input_typeids[i])
-				nmatch++;
-		}
-
-		/* take this one as the best choice so far? */
-		if ((nmatch > nbestMatch) || (last_candidate == NULL))
-		{
-			nbestMatch = nmatch;
-			candidates = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates = 1;
-		}
-		/* no worse than the last choice, so keep this one too? */
-		else if (nmatch == nbestMatch)
-		{
-			last_candidate->next = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates++;
-		}
-		/* otherwise, don't bother keeping this one... */
-	}
-
-	if (last_candidate)			/* terminate rebuilt list */
-		last_candidate->next = NULL;
-
-	if (ncandidates == 1)
-		return candidates->oid;
-
-	/*
-	 * Still too many candidates? Run through all candidates and keep
-	 * those with the most matches on exact types + binary-compatible
-	 * types. Keep all candidates if none match.
-	 */
-	ncandidates = 0;
-	nbestMatch = 0;
-	last_candidate = NULL;
-	for (current_candidate = candidates;
-		 current_candidate != NULL;
-		 current_candidate = current_candidate->next)
-	{
-		current_typeids = current_candidate->args;
-		nmatch = 0;
-		for (i = 0; i < nargs; i++)
-		{
-			if (input_typeids[i] != UNKNOWNOID)
-			{
-				if (IsBinaryCoercible(input_typeids[i], current_typeids[i]))
-					nmatch++;
-			}
-		}
-
-		/* take this one as the best choice so far? */
-		if ((nmatch > nbestMatch) || (last_candidate == NULL))
-		{
-			nbestMatch = nmatch;
-			candidates = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates = 1;
-		}
-		/* no worse than the last choice, so keep this one too? */
-		else if (nmatch == nbestMatch)
-		{
-			last_candidate->next = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates++;
-		}
-		/* otherwise, don't bother keeping this one... */
-	}
-
-	if (last_candidate)			/* terminate rebuilt list */
-		last_candidate->next = NULL;
-
-	if (ncandidates == 1)
-		return candidates->oid;
-
-	/*
-	 * Still too many candidates? Now look for candidates which are
-	 * preferred types at the args that will require coercion. Keep all
-	 * candidates if none match.
-	 */
-	ncandidates = 0;
-	nbestMatch = 0;
-	last_candidate = NULL;
-	for (current_candidate = candidates;
-		 current_candidate != NULL;
-		 current_candidate = current_candidate->next)
-	{
-		current_typeids = current_candidate->args;
-		nmatch = 0;
-		for (i = 0; i < nargs; i++)
-		{
-			if (input_typeids[i] != UNKNOWNOID)
-			{
-				current_category = TypeCategory(current_typeids[i]);
-				if (current_typeids[i] == input_typeids[i] ||
-					IsPreferredType(current_category, current_typeids[i]))
-					nmatch++;
-			}
-		}
-
-		if ((nmatch > nbestMatch) || (last_candidate == NULL))
-		{
-			nbestMatch = nmatch;
-			candidates = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates = 1;
-		}
-		else if (nmatch == nbestMatch)
-		{
-			last_candidate->next = current_candidate;
-			last_candidate = current_candidate;
-			ncandidates++;
-		}
-	}
-
-	if (last_candidate)			/* terminate rebuilt list */
-		last_candidate->next = NULL;
-
-	if (ncandidates == 1)
-		return candidates->oid;
-
-	/*
-	 * Still too many candidates? Try assigning types for the unknown
-	 * columns.
-	 *
-	 * First try: if we have an unknown and a non-unknown input, see whether
-	 * there is a candidate all of whose input types are the same as the
-	 * known input type (there can be at most one such candidate).	If so,
-	 * use that candidate.	NOTE that this is cool only because operators
-	 * can't have more than 2 args, so taking the last non-unknown as
-	 * current_type can yield only one possibility if there is also an
-	 * unknown.
-	 */
-	unknownOids = FALSE;
-	current_type = UNKNOWNOID;
-	for (i = 0; i < nargs; i++)
-	{
-		if ((input_typeids[i] != UNKNOWNOID)
-			&& (input_typeids[i] != InvalidOid))
-			current_type = input_typeids[i];
-		else
-			unknownOids = TRUE;
-	}
-
-	if (unknownOids && (current_type != UNKNOWNOID))
-	{
-		for (current_candidate = candidates;
-			 current_candidate != NULL;
-			 current_candidate = current_candidate->next)
-		{
-			current_typeids = current_candidate->args;
-			nmatch = 0;
-			for (i = 0; i < nargs; i++)
-			{
-				if (current_type == current_typeids[i])
-					nmatch++;
-			}
-			if (nmatch == nargs)
-				return current_candidate->oid;
-		}
-	}
-
-	/*
-	 * Second try: same algorithm as for unknown resolution in
-	 * parse_func.c.
-	 *
-	 * We do this by examining each unknown argument position to see if we
-	 * can determine a "type category" for it.	If any candidate has an
-	 * input datatype of STRING category, use STRING category (this bias
-	 * towards STRING is appropriate since unknown-type literals look like
-	 * strings).  Otherwise, if all the candidates agree on the type
-	 * category of this argument position, use that category.  Otherwise,
-	 * fail because we cannot determine a category.
-	 *
-	 * If we are able to determine a type category, also notice whether any
-	 * of the candidates takes a preferred datatype within the category.
-	 *
-	 * Having completed this examination, remove candidates that accept the
-	 * wrong category at any unknown position.	Also, if at least one
-	 * candidate accepted a preferred type at a position, remove
-	 * candidates that accept non-preferred types.
-	 *
-	 * If we are down to one candidate at the end, we win.
+	 * Use the same heuristics as for ambiguous functions to resolve
+	 * the conflict.
 	 */
-	resolved_unknowns = false;
-	for (i = 0; i < nargs; i++)
-	{
-		bool		have_conflict;
-
-		if (input_typeids[i] != UNKNOWNOID)
-			continue;
-		resolved_unknowns = true;		/* assume we can do it */
-		slot_category[i] = INVALID_TYPE;
-		slot_has_preferred_type[i] = false;
-		have_conflict = false;
-		for (current_candidate = candidates;
-			 current_candidate != NULL;
-			 current_candidate = current_candidate->next)
-		{
-			current_typeids = current_candidate->args;
-			current_type = current_typeids[i];
-			current_category = TypeCategory(current_type);
-			if (slot_category[i] == INVALID_TYPE)
-			{
-				/* first candidate */
-				slot_category[i] = current_category;
-				slot_has_preferred_type[i] =
-					IsPreferredType(current_category, current_type);
-			}
-			else if (current_category == slot_category[i])
-			{
-				/* more candidates in same category */
-				slot_has_preferred_type[i] |=
-					IsPreferredType(current_category, current_type);
-			}
-			else
-			{
-				/* category conflict! */
-				if (current_category == STRING_TYPE)
-				{
-					/* STRING always wins if available */
-					slot_category[i] = current_category;
-					slot_has_preferred_type[i] =
-						IsPreferredType(current_category, current_type);
-				}
-				else
-				{
-					/*
-					 * Remember conflict, but keep going (might find
-					 * STRING)
-					 */
-					have_conflict = true;
-				}
-			}
-		}
-		if (have_conflict && slot_category[i] != STRING_TYPE)
-		{
-			/* Failed to resolve category conflict at this position */
-			resolved_unknowns = false;
-			break;
-		}
-	}
+	candidates = func_select_candidate(nargs, input_typeids, candidates);
 
-	if (resolved_unknowns)
-	{
-		/* Strip non-matching candidates */
-		ncandidates = 0;
-		last_candidate = NULL;
-		for (current_candidate = candidates;
-			 current_candidate != NULL;
-			 current_candidate = current_candidate->next)
-		{
-			bool		keepit = true;
-
-			current_typeids = current_candidate->args;
-			for (i = 0; i < nargs; i++)
-			{
-				if (input_typeids[i] != UNKNOWNOID)
-					continue;
-				current_type = current_typeids[i];
-				current_category = TypeCategory(current_type);
-				if (current_category != slot_category[i])
-				{
-					keepit = false;
-					break;
-				}
-				if (slot_has_preferred_type[i] &&
-					!IsPreferredType(current_category, current_type))
-				{
-					keepit = false;
-					break;
-				}
-			}
-			if (keepit)
-			{
-				/* keep this candidate */
-				last_candidate = current_candidate;
-				ncandidates++;
-			}
-			else
-			{
-				/* forget this candidate */
-				if (last_candidate)
-					last_candidate->next = current_candidate->next;
-				else
-					candidates = current_candidate->next;
-			}
-		}
-		if (last_candidate)		/* terminate rebuilt list */
-			last_candidate->next = NULL;
-	}
-
-	if (ncandidates == 1)
+	if (candidates)
 		return candidates->oid;
 
-	return InvalidOid;			/* failed to determine a unique candidate */
+	return InvalidOid;			/* failed to select a best candidate */
 }	/* oper_select_candidate() */
 
 
@@ -751,7 +366,7 @@ oper(List *opname, Oid ltypeId, Oid rtypeId, bool noError)
 
 			/*
 			 * Unspecified type for one of the arguments? then use the
-			 * other
+			 * other (XXX this is probably dead code?)
 			 */
 			if (rtypeId == InvalidOid)
 				rtypeId = ltypeId;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 5ff4b1931da9616e141caacf4843b0a626ef3701..77ef33b878358670ca47ae553158265a32358e5b 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.137 2003/05/15 15:50:18 petere Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.138 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,9 +77,11 @@
 #include <math.h>
 
 #include "access/heapam.h"
+#include "access/nbtree.h"
 #include "access/tuptoaster.h"
 #include "catalog/catname.h"
 #include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_statistic.h"
@@ -177,10 +179,9 @@ static bool get_restriction_var(List *args, int varRelid,
 					Var **var, Node **other,
 					bool *varonleft);
 static void get_join_vars(List *args, Var **var1, Var **var2);
-static Selectivity prefix_selectivity(Query *root, Var *var, Oid vartype,
-									  Const *prefix);
+static Selectivity prefix_selectivity(Query *root, Var *var,
+									  Oid opclass, Const *prefix);
 static Selectivity pattern_selectivity(Const *patt, Pattern_Type ptype);
-static Oid	find_operator(const char *opname, Oid datatype);
 static Datum string_to_datum(const char *str, Oid datatype);
 static Const *string_to_const(const char *str, Oid datatype);
 
@@ -837,6 +838,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 	Datum		constval;
 	Oid			consttype;
 	Oid			vartype;
+	Oid			opclass;
 	Pattern_Prefix_Status pstatus;
 	Const	   *patt = NULL;
 	Const	   *prefix = NULL;
@@ -884,21 +886,77 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 	if (vartype != consttype)
 		vartype = getBaseType(vartype);
 
+	/*
+	 * We should now be able to recognize the var's datatype.  Choose the
+	 * index opclass from which we must draw the comparison operators.
+	 *
+	 * NOTE: It would be more correct to use the PATTERN opclasses than
+	 * the simple ones, but at the moment ANALYZE will not generate statistics
+	 * for the PATTERN operators.  But our results are so approximate anyway
+	 * that it probably hardly matters.
+	 */
+	switch (vartype)
+	{
+		case TEXTOID:
+			opclass = TEXT_BTREE_OPS_OID;
+			break;
+		case VARCHAROID:
+			opclass = VARCHAR_BTREE_OPS_OID;
+			break;
+		case BPCHAROID:
+			opclass = BPCHAR_BTREE_OPS_OID;
+			break;
+		case NAMEOID:
+			opclass = NAME_BTREE_OPS_OID;
+			break;
+		case BYTEAOID:
+			opclass = BYTEA_BTREE_OPS_OID;
+			break;
+		default:
+			return DEFAULT_MATCH_SEL;
+	}
+
 	/* divide pattern into fixed prefix and remainder */
 	patt = (Const *) other;
 	pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest);
 
+	/*
+	 * If necessary, coerce the prefix constant to the right type.
+	 * (The "rest" constant need not be changed.)
+	 */
+	if (prefix && prefix->consttype != vartype)
+	{
+		char   *prefixstr;
+
+		switch (prefix->consttype)
+		{
+			case TEXTOID:
+				prefixstr = DatumGetCString(DirectFunctionCall1(textout,
+															 prefix->constvalue));
+				break;
+			case BYTEAOID:
+				prefixstr = DatumGetCString(DirectFunctionCall1(byteaout,
+															 prefix->constvalue));
+				break;
+			default:
+				elog(ERROR, "patternsel: unexpected consttype %u",
+					 prefix->consttype);
+				return DEFAULT_MATCH_SEL;
+		}
+		prefix = string_to_const(prefixstr, vartype);
+		pfree(prefixstr);
+	}
+
 	if (pstatus == Pattern_Prefix_Exact)
 	{
 		/*
 		 * Pattern specifies an exact match, so pretend operator is '='
 		 */
-		Oid			eqopr = find_operator("=", vartype);
+		Oid			eqopr = get_opclass_member(opclass, BTEqualStrategyNumber);
 		List	   *eqargs;
 
 		if (eqopr == InvalidOid)
-			elog(ERROR, "patternsel: no = operator for type %u",
-				 vartype);
+			elog(ERROR, "patternsel: no = operator for opclass %u", opclass);
 		eqargs = makeList2(var, prefix);
 		result = DatumGetFloat8(DirectFunctionCall4(eqsel,
 													PointerGetDatum(root),
@@ -918,7 +976,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 		Selectivity selec;
 
 		if (pstatus == Pattern_Prefix_Partial)
-			prefixsel = prefix_selectivity(root, var, vartype, prefix);
+			prefixsel = prefix_selectivity(root, var, opclass, prefix);
 		else
 			prefixsel = 1.0;
 		restsel = pattern_selectivity(rest, ptype);
@@ -3020,10 +3078,13 @@ get_join_vars(List *args, Var **var1, Var **var2)
 
 /*
  * Extract the fixed prefix, if any, for a pattern.
- * *prefix is set to a palloc'd prefix string,
- * or to NULL if no fixed prefix exists for the pattern.
- * *rest is set to point to the remainder of the pattern after the
- * portion describing the fixed prefix.
+ *
+ * *prefix is set to a palloc'd prefix string (in the form of a Const node),
+ *	or to NULL if no fixed prefix exists for the pattern.
+ * *rest is set to a palloc'd Const representing the remainder of the pattern
+ *	after the portion describing the fixed prefix.
+ * Each of these has the same type (TEXT or BYTEA) as the given pattern Const.
+ *
  * The return value distinguishes no fixed prefix, a partial prefix,
  * or an exact-match-only pattern.
  */
@@ -3035,7 +3096,6 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive,
 	char	   *match;
 	char	   *patt;
 	int			pattlen;
-	char	   *prefix;
 	char	   *rest;
 	Oid			typeid = patt_const->consttype;
 	int			pos,
@@ -3058,7 +3118,7 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive,
 		pattlen = toast_raw_datum_size(patt_const->constvalue) - VARHDRSZ;
 	}
 
-	prefix = match = palloc(pattlen + 1);
+	match = palloc(pattlen + 1);
 	match_pos = 0;
 
 	for (pos = 0; pos < pattlen; pos++)
@@ -3093,12 +3153,11 @@ like_fixed_prefix(Const *patt_const, bool case_insensitive,
 	match[match_pos] = '\0';
 	rest = &patt[pos];
 
-	*prefix_const = string_to_const(prefix, typeid);
+	*prefix_const = string_to_const(match, typeid);
 	*rest_const = string_to_const(rest, typeid);
 
 	pfree(patt);
 	pfree(match);
-	prefix = NULL;
 
 	/* in LIKE, an empty pattern is an exact match! */
 	if (pos == pattlen)
@@ -3120,7 +3179,6 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive,
 				match_pos,
 				paren_depth;
 	char	   *patt;
-	char	   *prefix;
 	char	   *rest;
 	Oid			typeid = patt_const->consttype;
 
@@ -3176,7 +3234,7 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive,
 	}
 
 	/* OK, allocate space for pattern */
-	prefix = match = palloc(strlen(patt) + 1);
+	match = palloc(strlen(patt) + 1);
 	match_pos = 0;
 
 	/* note start at pos 1 to skip leading ^ */
@@ -3231,18 +3289,20 @@ regex_fixed_prefix(Const *patt_const, bool case_insensitive,
 	{
 		rest = &patt[pos + 1];
 
-		*prefix_const = string_to_const(prefix, typeid);
+		*prefix_const = string_to_const(match, typeid);
 		*rest_const = string_to_const(rest, typeid);
 
+		pfree(patt);
+		pfree(match);
+
 		return Pattern_Prefix_Exact;	/* pattern specifies exact match */
 	}
 
-	*prefix_const = string_to_const(prefix, typeid);
+	*prefix_const = string_to_const(match, typeid);
 	*rest_const = string_to_const(rest, typeid);
 
 	pfree(patt);
 	pfree(match);
-	prefix = NULL;
 
 	if (match_pos > 0)
 		return Pattern_Prefix_Partial;
@@ -3284,10 +3344,8 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
  * A fixed prefix "foo" is estimated as the selectivity of the expression
  * "var >= 'foo' AND var < 'fop'" (see also indxqual.c).
  *
- * Because of constant-folding, we can assume that the prefixcon constant's
- * type exactly matches the operator's declared input type; but it's not
- * safe to make the same assumption for the Var, so the type to use for the
- * Var must be passed in separately.
+ * We use the >= and < operators from the specified btree opclass to do the
+ * estimation.  The given Var and Const must be of the associated datatype.
  *
  * XXX Note: we make use of the upper bound to estimate operator selectivity
  * even if the locale is such that we cannot rely on the upper-bound string.
@@ -3295,27 +3353,17 @@ pattern_fixed_prefix(Const *patt, Pattern_Type ptype,
  * more useful to use the upper-bound code than not.
  */
 static Selectivity
-prefix_selectivity(Query *root, Var *var, Oid vartype, Const *prefixcon)
+prefix_selectivity(Query *root, Var *var, Oid opclass, Const *prefixcon)
 {
 	Selectivity prefixsel;
 	Oid			cmpopr;
-	char	   *prefix;
 	List	   *cmpargs;
 	Const	   *greaterstrcon;
 
-	cmpopr = find_operator(">=", vartype);
+	cmpopr = get_opclass_member(opclass, BTGreaterEqualStrategyNumber);
 	if (cmpopr == InvalidOid)
-		elog(ERROR, "prefix_selectivity: no >= operator for type %u",
-			 vartype);
-	if (prefixcon->consttype != BYTEAOID)
-		prefix = DatumGetCString(DirectFunctionCall1(textout, prefixcon->constvalue));
-	else
-		prefix = DatumGetCString(DirectFunctionCall1(byteaout, prefixcon->constvalue));
-
-	/* If var is type NAME, must adjust type of comparison constant */
-	if (vartype == NAMEOID)
-		prefixcon = string_to_const(prefix, NAMEOID);
-
+		elog(ERROR, "prefix_selectivity: no >= operator for opclass %u",
+			 opclass);
 	cmpargs = makeList2(var, prefixcon);
 	/* Assume scalargtsel is appropriate for all supported types */
 	prefixsel = DatumGetFloat8(DirectFunctionCall4(scalargtsel,
@@ -3334,10 +3382,10 @@ prefix_selectivity(Query *root, Var *var, Oid vartype, Const *prefixcon)
 	{
 		Selectivity topsel;
 
-		cmpopr = find_operator("<", vartype);
+		cmpopr = get_opclass_member(opclass, BTLessStrategyNumber);
 		if (cmpopr == InvalidOid)
-			elog(ERROR, "prefix_selectivity: no < operator for type %u",
-				 vartype);
+			elog(ERROR, "prefix_selectivity: no < operator for opclass %u",
+				 opclass);
 		cmpargs = makeList2(var, greaterstrcon);
 		/* Assume scalarltsel is appropriate for all supported types */
 		topsel = DatumGetFloat8(DirectFunctionCall4(scalarltsel,
@@ -3702,18 +3750,6 @@ make_greater_string(const Const *str_const)
 	return (Const *) NULL;
 }
 
-/* See if there is a binary op of the given name for the given datatype */
-/* NB: we assume that only built-in system operators are searched for */
-static Oid
-find_operator(const char *opname, Oid datatype)
-{
-	return GetSysCacheOid(OPERNAMENSP,
-						  PointerGetDatum(opname),
-						  ObjectIdGetDatum(datatype),
-						  ObjectIdGetDatum(datatype),
-						  ObjectIdGetDatum(PG_CATALOG_NAMESPACE));
-}
-
 /*
  * Generate a Datum of the appropriate type from a C string.
  * Note that all of the supported types are pass-by-ref, so the
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 77fb26a480793f5a11d604c7ab7453782aac0f77..5085c6025c2aef05112531a0db10ad45a0500e39 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.96 2003/05/12 23:08:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.97 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -343,7 +343,10 @@ name_bpchar(PG_FUNCTION_ARGS)
 
 
 /*****************************************************************************
- *	 varchar - varchar()													 *
+ *	 varchar - varchar(n)
+ *
+ * Note: varchar piggybacks on type text for most operations, and so has no
+ * C-coded functions except for I/O and typmod checking.
  *****************************************************************************/
 
 /*
@@ -700,7 +703,7 @@ bpcharcmp(PG_FUNCTION_ARGS)
 
 /*
  * bpchar needs a specialized hash function because we want to ignore
- * trailing blanks in comparisons.	(varchar can use plain hashvarlena.)
+ * trailing blanks in comparisons.
  */
 Datum
 hashbpchar(PG_FUNCTION_ARGS)
@@ -720,187 +723,3 @@ hashbpchar(PG_FUNCTION_ARGS)
 
 	return result;
 }
-
-
-/*****************************************************************************
- *	Functions used for varchar
- *****************************************************************************/
-
-Datum
-varcharlen(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg = PG_GETARG_VARCHAR_P(0);
-
-	/* optimization for single byte encoding */
-	if (pg_database_encoding_max_length() <= 1)
-		PG_RETURN_INT32(VARSIZE(arg) - VARHDRSZ);
-
-	PG_RETURN_INT32(
-			  pg_mbstrlen_with_len(VARDATA(arg), VARSIZE(arg) - VARHDRSZ)
-		);
-}
-
-Datum
-varcharoctetlen(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg = PG_GETARG_VARCHAR_P(0);
-
-	PG_RETURN_INT32(VARSIZE(arg) - VARHDRSZ);
-}
-
-
-/*****************************************************************************
- *	Comparison Functions used for varchar
- *
- * Note: btree indexes need these routines not to leak memory; therefore,
- * be careful to free working copies of toasted datums.  Most places don't
- * need to be so careful.
- *****************************************************************************/
-
-Datum
-varchareq(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	bool		result;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	/* fast path for different-length inputs */
-	if (len1 != len2)
-		result = false;
-	else
-		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) == 0);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(result);
-}
-
-Datum
-varcharne(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	bool		result;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	/* fast path for different-length inputs */
-	if (len1 != len2)
-		result = true;
-	else
-		result = (varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2) != 0);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(result);
-}
-
-Datum
-varcharlt(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	int			cmp;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(cmp < 0);
-}
-
-Datum
-varcharle(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	int			cmp;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(cmp <= 0);
-}
-
-Datum
-varchargt(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	int			cmp;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(cmp > 0);
-}
-
-Datum
-varcharge(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	int			cmp;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_BOOL(cmp >= 0);
-}
-
-Datum
-varcharcmp(PG_FUNCTION_ARGS)
-{
-	VarChar    *arg1 = PG_GETARG_VARCHAR_P(0);
-	VarChar    *arg2 = PG_GETARG_VARCHAR_P(1);
-	int			len1,
-				len2;
-	int			cmp;
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
-
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
-
-	PG_FREE_IF_COPY(arg1, 0);
-	PG_FREE_IF_COPY(arg2, 1);
-
-	PG_RETURN_INT32(cmp);
-}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 19178cc5243f3cfa36a0d928a44a5a3b2d9a6b44..fcd9dc2f59bbd19528a88da6979f03b57d4d81f1 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.94 2003/05/13 04:38:58 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.95 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -80,6 +80,33 @@ op_requires_recheck(Oid opno, Oid opclass)
 	return result;
 }
 
+/*
+ * get_opclass_member
+ *		Get the OID of the operator that implements the specified strategy
+ *		for the specified opclass.
+ *
+ * Returns InvalidOid if there is no pg_amop entry for the given keys.
+ */
+Oid
+get_opclass_member(Oid opclass, int16 strategy)
+{
+	HeapTuple	tp;
+	Form_pg_amop amop_tup;
+	Oid			result;
+
+	tp = SearchSysCache(AMOPSTRATEGY,
+						ObjectIdGetDatum(opclass),
+						Int16GetDatum(strategy),
+						0, 0);
+	if (!HeapTupleIsValid(tp))
+		return InvalidOid;
+	amop_tup = (Form_pg_amop) GETSTRUCT(tp);
+	result = amop_tup->amopopr;
+	ReleaseSysCache(tp);
+	return result;
+}
+
+
 /*				---------- ATTRIBUTE CACHES ----------					 */
 
 /*
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index c7848af9f62f49cc22a1fca87daf8f4bf17c5860..2c28ffcabc55fefc4e1e1c7c18a072f907acbe59 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.195 2003/05/23 22:33:22 tgl Exp $
+ * $Id: catversion.h,v 1.196 2003/05/26 00:11:27 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200305231
+#define CATALOG_VERSION_NO	200305241
 
 #endif
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index b373ce15a223d77c3d5e7ec504d710bf16eba065..dbff38b3c392bc3fda7db669da54a32773979341 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.48 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_amop.h,v 1.49 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	 the genbki.sh script reads this file and generates .bki
@@ -219,14 +219,14 @@ DATA(insert (	 426 4 f 1061 ));
 DATA(insert (	 426 5 f 1060 ));
 
 /*
- *	btree varchar_ops
+ *	btree varchar_ops (same operators as text_ops)
  */
 
-DATA(insert (	2003 1 f 1066 ));
-DATA(insert (	2003 2 f 1067 ));
-DATA(insert (	2003 3 f 1062 ));
-DATA(insert (	2003 4 f 1069 ));
-DATA(insert (	2003 5 f 1068 ));
+DATA(insert (	2003 1 f 664 ));
+DATA(insert (	2003 2 f 665 ));
+DATA(insert (	2003 3 f  98 ));
+DATA(insert (	2003 4 f 667 ));
+DATA(insert (	2003 5 f 666 ));
 
 /*
  *	btree bytea_ops
@@ -389,14 +389,14 @@ DATA(insert (	2095 4 f 2317 ));
 DATA(insert (	2095 5 f 2318 ));
 
 /*
- *	btree varchar pattern
+ *	btree varchar pattern (same operators as text)
  */
 
-DATA(insert (	2096 1 f 2320 ));
-DATA(insert (	2096 2 f 2321 ));
-DATA(insert (	2096 3 f 2322 ));
-DATA(insert (	2096 4 f 2323 ));
-DATA(insert (	2096 5 f 2324 ));
+DATA(insert (	2096 1 f 2314 ));
+DATA(insert (	2096 2 f 2315 ));
+DATA(insert (	2096 3 f 2316 ));
+DATA(insert (	2096 4 f 2317 ));
+DATA(insert (	2096 5 f 2318 ));
 
 /*
  *	btree bpchar pattern
@@ -462,7 +462,7 @@ DATA(insert (	1999 1 f 1320 ));
 /* timetz_ops */
 DATA(insert (	2001 1 f 1550 ));
 /* varchar_ops */
-DATA(insert (	2004 1 f 1062 ));
+DATA(insert (	2004 1 f   98 ));
 /* timestamp_ops */
 DATA(insert (	2040 1 f 2060 ));
 
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index ba15ac9a6603ce33dc7527c7c334e2ea252bf87e..0048d000cdf90f18bd97c75234c9f5f50b4d50be 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.36 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_amproc.h,v 1.37 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -103,10 +103,10 @@ DATA(insert (	1996 1 1107 ));
 DATA(insert (	1998 1 1314 ));
 DATA(insert (	2000 1 1358 ));
 DATA(insert (	2002 1 1672 ));
-DATA(insert (	2003 1 1079 ));
-DATA(insert (	2039 1 1314 ));
+DATA(insert (	2003 1  360 ));
+DATA(insert (	2039 1 2045 ));
 DATA(insert (	2095 1 2166 ));
-DATA(insert (	2096 1 2173 ));
+DATA(insert (	2096 1 2166 ));
 DATA(insert (	2097 1 2180 ));
 DATA(insert (	2098 1 2187 ));
 
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index ed6834c6683698e2b32fe674c104490734ffcc3d..6c3d47ba7f149a0a526756ca1a3be9dc4691914d 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
- * $Id: pg_cast.h,v 1.6 2003/05/14 18:08:15 tgl Exp $
+ * $Id: pg_cast.h,v 1.7 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -161,8 +161,8 @@ DATA(insert ( 2206	 23    0 a ));
  */
 DATA(insert (	25 1042    0 i ));
 DATA(insert (	25 1043    0 i ));
-DATA(insert ( 1042	 25    0 i ));
-DATA(insert ( 1042 1043    0 i ));
+DATA(insert ( 1042	 25  401 i ));
+DATA(insert ( 1042 1043  401 i ));
 DATA(insert ( 1043	 25    0 i ));
 DATA(insert ( 1043 1042    0 i ));
 DATA(insert (	18	 25  946 i ));
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index 29c92f5ddfbdaf0775a147a4b9727e86dd6bac71..809cde3da02ec4052cf8a25340407b59269b3894 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -9,7 +9,7 @@
  * of opclass name and index access method type.  This row specifies the
  * expected input data type for the opclass (the type of the heap column,
  * or the function output type in the case of a functional index).	Note
- * that types binary-compatible with the specified type will be accepted too.
+ * that types binary-coercible to the specified type will be accepted too.
  *
  * For a given <opcamid, opcintype> pair, there can be at most one row that
  * has opcdefault = true; this row is the default opclass for such data in
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.48 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_opclass.h,v 1.49 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -92,11 +92,14 @@ DATA(insert OID =  423 (	403		bit_ops			PGNSP PGUID 1560 t 0 ));
 DATA(insert OID =  424 (	403		bool_ops		PGNSP PGUID   16 t 0 ));
 DATA(insert OID =  425 (	402		box_ops			PGNSP PGUID  603 t 0 ));
 DATA(insert OID =  426 (	403		bpchar_ops		PGNSP PGUID 1042 t 0 ));
+#define BPCHAR_BTREE_OPS_OID 426
 DATA(insert OID =  427 (	405		bpchar_ops		PGNSP PGUID 1042 t 0 ));
 DATA(insert OID =  428 (	403		bytea_ops		PGNSP PGUID   17 t 0 ));
+#define BYTEA_BTREE_OPS_OID 428
 DATA(insert OID =  429 (	403		char_ops		PGNSP PGUID   18 t 0 ));
 DATA(insert OID =  431 (	405		char_ops		PGNSP PGUID   18 t 0 ));
 DATA(insert OID =  432 (	403		cidr_ops		PGNSP PGUID  650 t 0 ));
+#define CIDR_BTREE_OPS_OID 432
 DATA(insert OID =  433 (	405		cidr_ops		PGNSP PGUID  650 t 0 ));
 DATA(insert OID =  434 (	403		date_ops		PGNSP PGUID 1082 t 0 ));
 DATA(insert OID =  435 (	405		date_ops		PGNSP PGUID 1082 t 0 ));
@@ -105,6 +108,7 @@ DATA(insert OID = 1971 (	405		float4_ops		PGNSP PGUID  700 t 0 ));
 DATA(insert OID = 1972 (	403		float8_ops		PGNSP PGUID  701 t 0 ));
 DATA(insert OID = 1973 (	405		float8_ops		PGNSP PGUID  701 t 0 ));
 DATA(insert OID = 1974 (	403		inet_ops		PGNSP PGUID  869 t 0 ));
+#define INET_BTREE_OPS_OID 1974
 DATA(insert OID = 1975 (	405		inet_ops		PGNSP PGUID  869 t 0 ));
 DATA(insert OID = 1976 (	403		int2_ops		PGNSP PGUID   21 t 0 ));
 #define INT2_BTREE_OPS_OID 1976
@@ -119,6 +123,7 @@ DATA(insert OID = 1983 (	405		interval_ops	PGNSP PGUID 1186 t 0 ));
 DATA(insert OID = 1984 (	403		macaddr_ops		PGNSP PGUID  829 t 0 ));
 DATA(insert OID = 1985 (	405		macaddr_ops		PGNSP PGUID  829 t 0 ));
 DATA(insert OID = 1986 (	403		name_ops		PGNSP PGUID   19 t 0 ));
+#define NAME_BTREE_OPS_OID 1986
 DATA(insert OID = 1987 (	405		name_ops		PGNSP PGUID   19 t 0 ));
 DATA(insert OID = 1988 (	403		numeric_ops		PGNSP PGUID 1700 t 0 ));
 DATA(insert OID = 1989 (	403		oid_ops			PGNSP PGUID   26 t 0 ));
@@ -128,6 +133,7 @@ DATA(insert OID = 1991 (	403		oidvector_ops	PGNSP PGUID   30 t 0 ));
 DATA(insert OID = 1992 (	405		oidvector_ops	PGNSP PGUID   30 t 0 ));
 DATA(insert OID = 1993 (	402		poly_ops		PGNSP PGUID  604 t 0 ));
 DATA(insert OID = 1994 (	403		text_ops		PGNSP PGUID   25 t 0 ));
+#define TEXT_BTREE_OPS_OID 1994
 DATA(insert OID = 1995 (	405		text_ops		PGNSP PGUID   25 t 0 ));
 DATA(insert OID = 1996 (	403		time_ops		PGNSP PGUID 1083 t 0 ));
 DATA(insert OID = 1997 (	405		time_ops		PGNSP PGUID 1083 t 0 ));
@@ -137,12 +143,17 @@ DATA(insert OID = 2000 (	403		timetz_ops		PGNSP PGUID 1266 t 0 ));
 DATA(insert OID = 2001 (	405		timetz_ops		PGNSP PGUID 1266 t 0 ));
 DATA(insert OID = 2002 (	403		varbit_ops		PGNSP PGUID 1562 t 0 ));
 DATA(insert OID = 2003 (	403		varchar_ops		PGNSP PGUID 1043 t 0 ));
+#define VARCHAR_BTREE_OPS_OID 2003
 DATA(insert OID = 2004 (	405		varchar_ops		PGNSP PGUID 1043 t 0 ));
 DATA(insert OID = 2039 (	403		timestamp_ops	PGNSP PGUID 1114 t 0 ));
 DATA(insert OID = 2040 (	405		timestamp_ops	PGNSP PGUID 1114 t 0 ));
 DATA(insert OID = 2095 (	403		text_pattern_ops	PGNSP PGUID   25 f 0 ));
+#define TEXT_PATTERN_BTREE_OPS_OID 2095
 DATA(insert OID = 2096 (	403		varchar_pattern_ops	PGNSP PGUID 1043 f 0 ));
+#define VARCHAR_PATTERN_BTREE_OPS_OID 2096
 DATA(insert OID = 2097 (	403		bpchar_pattern_ops	PGNSP PGUID 1042 f 0 ));
+#define BPCHAR_PATTERN_BTREE_OPS_OID 2097
 DATA(insert OID = 2098 (	403		name_pattern_ops	PGNSP PGUID   19 f 0 ));
+#define NAME_PATTERN_BTREE_OPS_OID 2098
 
 #endif   /* PG_OPCLASS_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 2fe0da5fbe9dbf6bd30c74347ddfea70929c337f..ae4fb6e04bb978bbad0b2f2ca5cd9ac6b100f268 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.113 2003/05/15 15:50:19 petere Exp $
+ * $Id: pg_operator.h,v 1.114 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -122,7 +122,9 @@ DATA(insert OID = 374 (  "||"	   PGNSP PGUID b f 2283 2277 2277   0 0 0 0	0 0 ar
 DATA(insert OID = 375 (  "||"	   PGNSP PGUID b f 2277 2277 2277   0 0 0 0	0 0 array_cat      -       -     ));
 
 DATA(insert OID = 352 (  "="	   PGNSP PGUID b t	28	28	16 352	 0	 0	 0	 0	 0 xideq eqsel eqjoinsel ));
-DATA(insert OID = 353 (  "="	   PGNSP PGUID b t	28	23	16	 0	 0	 0	 0	 0	 0 xideq eqsel eqjoinsel ));
+DATA(insert OID = 353 (  "="	   PGNSP PGUID b t	28	23	16	 0	 0	 0	 0	 0	 0 xideqint4 eqsel eqjoinsel ));
+DATA(insert OID = 385 (  "="	   PGNSP PGUID b t	29	29	16 385	 0	 0	 0	 0	 0 cideq eqsel eqjoinsel ));
+DATA(insert OID = 386 (  "="	   PGNSP PGUID b t	22	22	16 386	 0	 0	 0	 0	 0 int2vectoreq eqsel eqjoinsel ));
 DATA(insert OID = 387 (  "="	   PGNSP PGUID b t	27	27	16 387	 0	 0	 0	 0	 0 tideq eqsel eqjoinsel ));
 #define TIDEqualOperator   387
 DATA(insert OID = 388 (  "!"	   PGNSP PGUID r f	20	 0	20	 0	 0	 0	 0	 0	 0 int8fac - - ));
@@ -170,7 +172,7 @@ DATA(insert OID = 506 (  ">^"	   PGNSP PGUID b f 600 600	16	 0	 0	 0	 0	 0	 0 po
 DATA(insert OID = 507 (  "<<"	   PGNSP PGUID b f 600 600	16	 0	 0	 0	 0	 0	 0 point_left positionsel positionjoinsel ));
 DATA(insert OID = 508 (  ">>"	   PGNSP PGUID b f 600 600	16	 0	 0	 0	 0	 0	 0 point_right positionsel positionjoinsel ));
 DATA(insert OID = 509 (  "<^"	   PGNSP PGUID b f 600 600	16	 0	 0	 0	 0	 0	 0 point_below positionsel positionjoinsel ));
-DATA(insert OID = 510 (  "~="	   PGNSP PGUID b f 600 600	16 510	 0	 0	 0	 0	 0 point_eq eqsel eqjoinsel ));
+DATA(insert OID = 510 (  "~="	   PGNSP PGUID b f 600 600	16 510 713	 0	 0	 0	 0 point_eq eqsel eqjoinsel ));
 DATA(insert OID = 511 (  "@"	   PGNSP PGUID b f 600 603	16	 0	 0	 0	 0	 0	 0 on_pb - - ));
 DATA(insert OID = 512 (  "@"	   PGNSP PGUID b f 600 602	16 755	 0	 0	 0	 0	 0 on_ppath - - ));
 DATA(insert OID = 513 (  "@@"	   PGNSP PGUID l f	 0 603 600	 0	 0	 0	 0	 0	 0 box_center - - ));
@@ -350,6 +352,8 @@ DATA(insert OID = 708 (  "<->"	   PGNSP PGUID b f 628 628 701 708	 0	0  0   0
 DATA(insert OID = 709 (  "<->"	   PGNSP PGUID b f 601 601 701 709	 0	0  0   0   0 lseg_distance - - ));
 DATA(insert OID = 712 (  "<->"	   PGNSP PGUID b f 604 604 701 712	 0	0  0   0   0 poly_distance - - ));
 
+DATA(insert OID = 713 (  "<>"	   PGNSP PGUID b f 600 600	16 713 510  0  0   0   0 point_ne neqsel neqjoinsel ));
+
 /* add translation/rotation/scaling operators for geometric types. - thomas 97/05/10 */
 DATA(insert OID = 731 (  "+"	   PGNSP PGUID b f	600  600	600  731  0 0 0 0 0 point_add - - ));
 DATA(insert OID = 732 (  "-"	   PGNSP PGUID b f	600  600	600    0  0 0 0 0 0 point_sub - - ));
@@ -427,29 +431,16 @@ DATA(insert OID =  969 (  "@@"	   PGNSP PGUID l f	0  601	600    0  0 0 0 0 0 lse
 DATA(insert OID =  970 (  "@@"	   PGNSP PGUID l f	0  602	600    0  0 0 0 0 0 path_center - - ));
 DATA(insert OID =  971 (  "@@"	   PGNSP PGUID l f	0  604	600    0  0 0 0 0 0 poly_center - - ));
 
-DATA(insert OID =  974 (  "||"	   PGNSP PGUID b f 1042 1042 1042	 0	0 0 0 0 0 textcat - - ));
-DATA(insert OID =  979 (  "||"	   PGNSP PGUID b f 1043 1043 1043	 0	0 0 0 0 0 textcat - - ));
-
 DATA(insert OID = 1054 ( "="	   PGNSP PGUID b f 1042 1042	 16 1054 1057 1058 1058 1058 1060 bpchareq eqsel eqjoinsel ));
-DATA(insert OID = 1055 ( "~"	   PGNSP PGUID b f 1042 25	 16    0 1056  0 0 0 0 textregexeq regexeqsel regexeqjoinsel ));
+DATA(insert OID = 1055 ( "~"	   PGNSP PGUID b f 1042 25	 16    0 1056  0 0 0 0 bpcharregexeq regexeqsel regexeqjoinsel ));
 #define OID_BPCHAR_REGEXEQ_OP		1055
-DATA(insert OID = 1056 ( "!~"	   PGNSP PGUID b f 1042 25	 16    0 1055  0 0 0 0 textregexne regexnesel regexnejoinsel ));
+DATA(insert OID = 1056 ( "!~"	   PGNSP PGUID b f 1042 25	 16    0 1055  0 0 0 0 bpcharregexne regexnesel regexnejoinsel ));
 DATA(insert OID = 1057 ( "<>"	   PGNSP PGUID b f 1042 1042	 16 1057 1054  0 0 0 0 bpcharne neqsel neqjoinsel ));
 DATA(insert OID = 1058 ( "<"	   PGNSP PGUID b f 1042 1042	 16 1060 1061  0 0 0 0 bpcharlt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1059 ( "<="	   PGNSP PGUID b f 1042 1042	 16 1061 1060  0 0 0 0 bpcharle scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1060 ( ">"	   PGNSP PGUID b f 1042 1042	 16 1058 1059  0 0 0 0 bpchargt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1061 ( ">="	   PGNSP PGUID b f 1042 1042	 16 1059 1058  0 0 0 0 bpcharge scalargtsel scalargtjoinsel ));
 
-DATA(insert OID = 1062 ( "="	   PGNSP PGUID b t 1043 1043	16	1062 1065 1066 1066 1066 1068 varchareq eqsel eqjoinsel ));
-DATA(insert OID = 1063 ( "~"	   PGNSP PGUID b f 1043 25	16 0 1064  0 0 0 0 textregexeq regexeqsel regexeqjoinsel ));
-#define OID_VARCHAR_REGEXEQ_OP		1063
-DATA(insert OID = 1064 ( "!~"	   PGNSP PGUID b f 1043 25	16 0 1063  0 0 0 0 textregexne regexnesel regexnejoinsel ));
-DATA(insert OID = 1065 ( "<>"	   PGNSP PGUID b f 1043 1043	16 1065 1062  0 0 0 0 varcharne neqsel neqjoinsel ));
-DATA(insert OID = 1066 ( "<"	   PGNSP PGUID b f 1043 1043	16 1068 1069  0 0 0 0 varcharlt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1067 ( "<="	   PGNSP PGUID b f 1043 1043	16 1069 1068  0 0 0 0 varcharle scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1068 ( ">"	   PGNSP PGUID b f 1043 1043	16 1066 1067  0 0 0 0 varchargt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1069 ( ">="	   PGNSP PGUID b f 1043 1043	16 1067 1066  0 0 0 0 varcharge scalargtsel scalargtjoinsel ));
-
 /* date operators */
 DATA(insert OID = 1076 ( "+"	   PGNSP PGUID b f	1082	1186 1114 0 0 0 0 0 0 date_pl_interval - - ));
 DATA(insert OID = 1077 ( "-"	   PGNSP PGUID b f	1082	1186 1114 0 0 0 0 0 0 date_mi_interval - - ));
@@ -515,12 +506,9 @@ DATA(insert OID = 1208 (  "!~~"   PGNSP PGUID b f  19	25	16 0 1207 0 0 0 0 namen
 DATA(insert OID = 1209 (  "~~"	  PGNSP PGUID b f  25	25	16 0 1210 0 0 0 0 textlike likesel likejoinsel ));
 #define OID_TEXT_LIKE_OP		1209
 DATA(insert OID = 1210 (  "!~~"   PGNSP PGUID b f  25	25	16 0 1209 0 0 0 0 textnlike nlikesel nlikejoinsel ));
-DATA(insert OID = 1211 (  "~~"	  PGNSP PGUID b f  1042 25	16 0 1212 0 0 0 0 textlike likesel likejoinsel ));
+DATA(insert OID = 1211 (  "~~"	  PGNSP PGUID b f  1042 25	16 0 1212 0 0 0 0 bpcharlike likesel likejoinsel ));
 #define OID_BPCHAR_LIKE_OP		1211
-DATA(insert OID = 1212 (  "!~~"   PGNSP PGUID b f  1042 25	16 0 1211 0 0 0 0 textnlike nlikesel nlikejoinsel ));
-DATA(insert OID = 1213 (  "~~"	  PGNSP PGUID b f  1043 25	16 0 1214 0 0 0 0 textlike likesel likejoinsel ));
-#define OID_VARCHAR_LIKE_OP		1213
-DATA(insert OID = 1214 (  "!~~"   PGNSP PGUID b f  1043 25	16 0 1213 0 0 0 0 textnlike nlikesel nlikejoinsel ));
+DATA(insert OID = 1212 (  "!~~"   PGNSP PGUID b f  1042 25	16 0 1211 0 0 0 0 bpcharnlike nlikesel nlikejoinsel ));
 
 /* case-insensitive regex hacks */
 DATA(insert OID = 1226 (  "~*"		 PGNSP PGUID b f	19	25	16 0 1227  0 0 0 0 nameicregexeq icregexeqsel icregexeqjoinsel ));
@@ -529,20 +517,17 @@ DATA(insert OID = 1227 (  "!~*"		 PGNSP PGUID b f	19	25	16 0 1226  0 0 0 0 namei
 DATA(insert OID = 1228 (  "~*"		 PGNSP PGUID b f	25	25	16 0 1229  0 0 0 0 texticregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_TEXT_ICREGEXEQ_OP		1228
 DATA(insert OID = 1229 (  "!~*"		 PGNSP PGUID b f	25	25	16 0 1228  0 0 0 0 texticregexne icregexnesel icregexnejoinsel ));
-DATA(insert OID = 1232 (  "~*"		PGNSP PGUID b f  1043  25  16 0 1233	0 0   0   0 texticregexeq icregexeqsel icregexeqjoinsel ));
-#define OID_VARCHAR_ICREGEXEQ_OP		1232
-DATA(insert OID = 1233 ( "!~*"		PGNSP PGUID b f  1043  25  16 0 1232	0 0   0   0 texticregexne icregexnesel icregexnejoinsel ));
-DATA(insert OID = 1234 (  "~*"		PGNSP PGUID b f  1042  25  16 0 1235	0 0   0   0 texticregexeq icregexeqsel icregexeqjoinsel ));
+DATA(insert OID = 1234 (  "~*"		PGNSP PGUID b f  1042  25  16 0 1235	0 0   0   0 bpcharicregexeq icregexeqsel icregexeqjoinsel ));
 #define OID_BPCHAR_ICREGEXEQ_OP		1234
-DATA(insert OID = 1235 ( "!~*"		PGNSP PGUID b f  1042  25  16 0 1234	0 0   0   0 texticregexne icregexnesel icregexnejoinsel ));
+DATA(insert OID = 1235 ( "!~*"		PGNSP PGUID b f  1042  25  16 0 1234	0 0   0   0 bpcharicregexne icregexnesel icregexnejoinsel ));
 
 /* timestamptz operators */
-DATA(insert OID = 1320 (  "="	   PGNSP PGUID b f 1184 1184	 16 1320 1321 1322 1322 1322 1324 timestamp_eq eqsel eqjoinsel ));
-DATA(insert OID = 1321 (  "<>"	   PGNSP PGUID b f 1184 1184	 16 1321 1320 0 0 0 0 timestamp_ne neqsel neqjoinsel ));
-DATA(insert OID = 1322 (  "<"	   PGNSP PGUID b f 1184 1184	 16 1324 1325 0 0 0 0 timestamp_lt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1323 (  "<="	   PGNSP PGUID b f 1184 1184	 16 1325 1324 0 0 0 0 timestamp_le scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1324 (  ">"	   PGNSP PGUID b f 1184 1184	 16 1322 1323 0 0 0 0 timestamp_gt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1325 (  ">="	   PGNSP PGUID b f 1184 1184	 16 1323 1322 0 0 0 0 timestamp_ge scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1320 (  "="	   PGNSP PGUID b f 1184 1184	 16 1320 1321 1322 1322 1322 1324 timestamptz_eq eqsel eqjoinsel ));
+DATA(insert OID = 1321 (  "<>"	   PGNSP PGUID b f 1184 1184	 16 1321 1320 0 0 0 0 timestamptz_ne neqsel neqjoinsel ));
+DATA(insert OID = 1322 (  "<"	   PGNSP PGUID b f 1184 1184	 16 1324 1325 0 0 0 0 timestamptz_lt scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1323 (  "<="	   PGNSP PGUID b f 1184 1184	 16 1325 1324 0 0 0 0 timestamptz_le scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1324 (  ">"	   PGNSP PGUID b f 1184 1184	 16 1322 1323 0 0 0 0 timestamptz_gt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1325 (  ">="	   PGNSP PGUID b f 1184 1184	 16 1323 1322 0 0 0 0 timestamptz_ge scalargtsel scalargtjoinsel ));
 DATA(insert OID = 1327 (  "+"	   PGNSP PGUID b f 1184 1186 1184	 0	0 0 0 0 0 timestamptz_pl_span - - ));
 DATA(insert OID = 1328 (  "-"	   PGNSP PGUID b f 1184 1184 1186	 0	0 0 0 0 0 timestamptz_mi - - ));
 DATA(insert OID = 1329 (  "-"	   PGNSP PGUID b f 1184 1186 1184	 0	0 0 0 0 0 timestamptz_mi_span - - ));
@@ -691,20 +676,9 @@ DATA(insert OID = 1626 (  "!~~*"  PGNSP PGUID b f  19	25	16 0 1625 0 0 0 0 namei
 DATA(insert OID = 1627 (  "~~*"   PGNSP PGUID b f  25	25	16 0 1628 0 0 0 0 texticlike iclikesel iclikejoinsel ));
 #define OID_TEXT_ICLIKE_OP		1627
 DATA(insert OID = 1628 (  "!~~*"  PGNSP PGUID b f  25	25	16 0 1627 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-DATA(insert OID = 1629 (  "~~*"   PGNSP PGUID b f  1042 25	16 0 1630 0 0 0 0 texticlike iclikesel iclikejoinsel ));
+DATA(insert OID = 1629 (  "~~*"   PGNSP PGUID b f  1042 25	16 0 1630 0 0 0 0 bpchariclike iclikesel iclikejoinsel ));
 #define OID_BPCHAR_ICLIKE_OP	1629
-DATA(insert OID = 1630 (  "!~~*"  PGNSP PGUID b f  1042 25	16 0 1629 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-DATA(insert OID = 1631 (  "~~*"   PGNSP PGUID b f  1043 25	16 0 1632 0 0 0 0 texticlike iclikesel iclikejoinsel ));
-#define OID_VARCHAR_ICLIKE_OP	1631
-DATA(insert OID = 1632 (  "!~~*"  PGNSP PGUID b f  1043 25	16 0 1631 0 0 0 0 texticnlike icnlikesel icnlikejoinsel ));
-
-/* regproc comparisons --- use oid (unsigned) comparison */
-DATA(insert OID = 1656 (  "="	   PGNSP PGUID b t	24	24	16 1656 1657 1658 1658 1658 1659 oideq eqsel eqjoinsel ));
-DATA(insert OID = 1657 (  "<>"	   PGNSP PGUID b f	24	24	16 1657 1656 0 0 0 0 oidne neqsel neqjoinsel ));
-DATA(insert OID = 1658 (  "<"	   PGNSP PGUID b f	24	24	16 1659 1661 0 0 0 0 oidlt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1659 (  ">"	   PGNSP PGUID b f	24	24	16 1658 1660 0 0 0 0 oidgt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 1660 (  "<="	   PGNSP PGUID b f	24	24	16 1661 1659 0 0 0 0 oidle scalarltsel scalarltjoinsel ));
-DATA(insert OID = 1661 (  ">="	   PGNSP PGUID b f	24	24	16 1660 1658 0 0 0 0 oidge scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1630 (  "!~~*"  PGNSP PGUID b f  1042 25	16 0 1629 0 0 0 0 bpcharicnlike icnlikesel icnlikejoinsel ));
 
 /* NUMERIC type - OID's 1700-1799 */
 DATA(insert OID = 1751 (  "-"	   PGNSP PGUID l f	0 1700 1700    0	0 0 0 0 0 numeric_uminus - - ));
@@ -823,13 +797,6 @@ DATA(insert OID = 2317 ( "~>=~"	PGNSP PGUID b f 25 25 16 2315 2314 0 0 0 0 text_
 DATA(insert OID = 2318 ( "~>~"	PGNSP PGUID b f 25 25 16 2314 2315 0 0 0 0 text_pattern_gt scalargtsel scalargtjoinsel ));
 DATA(insert OID = 2319 ( "~<>~"	PGNSP PGUID b f 25 25 16 2319 2316 0 0 0 0 text_pattern_ne neqsel neqjoinsel ));
 
-DATA(insert OID = 2320 ( "~<~"	PGNSP PGUID b f 1043 1043 16 2324 2323 0 0 0 0 varchar_pattern_lt scalarltsel scalarltjoinsel ));
-DATA(insert OID = 2321 ( "~<=~"	PGNSP PGUID b f 1043 1043 16 2323 2324 0 0 0 0 varchar_pattern_le scalarltsel scalarltjoinsel ));
-DATA(insert OID = 2322 ( "~=~"	PGNSP PGUID b t 1043 1043 16 2322 2325 2320 2320 2320 2324 varchar_pattern_eq eqsel eqjoinsel ));
-DATA(insert OID = 2323 ( "~>=~"	PGNSP PGUID b f 1043 1043 16 2321 2320 0 0 0 0 varchar_pattern_ge scalargtsel scalargtjoinsel ));
-DATA(insert OID = 2324 ( "~>~"	PGNSP PGUID b f 1043 1043 16 2320 2321 0 0 0 0 varchar_pattern_gt scalargtsel scalargtjoinsel ));
-DATA(insert OID = 2325 ( "~<>~"	PGNSP PGUID b f 1043 1043 16 2325 2322 0 0 0 0 varchar_pattern_ne neqsel neqjoinsel ));
-
 DATA(insert OID = 2326 ( "~<~"	PGNSP PGUID b f 1042 1042 16 2330 2329 0 0 0 0 bpchar_pattern_lt scalarltsel scalarltjoinsel ));
 DATA(insert OID = 2327 ( "~<=~"	PGNSP PGUID b f 1042 1042 16 2329 2330 0 0 0 0 bpchar_pattern_le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 2328 ( "~=~"	PGNSP PGUID b t 1042 1042 16 2328 2331 2326 2326 2326 2330 bpchar_pattern_eq eqsel eqjoinsel ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f2f3e3eff14b0adc91e4ab317296efdb8abb5aba..7d6ff867962ac7f5e08d096d13ba5cdbb03a6876 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.301 2003/05/23 22:33:22 tgl Exp $
+ * $Id: pg_proc.h,v 1.302 2003/05/26 00:11:27 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -788,14 +788,16 @@ DESCR("intersect?");
 
 /* OIDS 400 - 499 */
 
+DATA(insert OID =  401 (  text			   PGNSP PGUID 12 f f t f i 1 25 "1042"  rtrim1 - _null_ ));
+DESCR("convert char(n) to text");
 DATA(insert OID =  406 (  text			   PGNSP PGUID 12 f f t f i 1 25 "19" name_text - _null_ ));
 DESCR("convert name to text");
 DATA(insert OID =  407 (  name			   PGNSP PGUID 12 f f t f i 1 19 "25" text_name - _null_ ));
 DESCR("convert text to name");
 DATA(insert OID =  408 (  bpchar		   PGNSP PGUID 12 f f t f i 1 1042 "19" name_bpchar - _null_ ));
-DESCR("convert name to char()");
+DESCR("convert name to char(n)");
 DATA(insert OID =  409 (  name			   PGNSP PGUID 12 f f t f i 1 19 "1042"  bpchar_name - _null_ ));
-DESCR("convert char() to name");
+DESCR("convert char(n) to name");
 
 DATA(insert OID = 440 (  hashgettuple	   PGNSP PGUID 12 f f t f v 2 16 "2281 2281"  hashgettuple - _null_ ));
 DESCR("hash(internal)");
@@ -1338,22 +1340,8 @@ DATA(insert OID = 1052 (  bpcharge		   PGNSP PGUID 12 f f t f i 2 16 "1042 1042"
 DESCR("greater-than-or-equal");
 DATA(insert OID = 1053 (  bpcharne		   PGNSP PGUID 12 f f t f i 2 16 "1042 1042"	bpcharne - _null_ ));
 DESCR("not equal");
-DATA(insert OID = 1070 (  varchareq		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varchareq - _null_ ));
-DESCR("equal");
-DATA(insert OID = 1071 (  varcharlt		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varcharlt - _null_ ));
-DESCR("less-than");
-DATA(insert OID = 1072 (  varcharle		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varcharle - _null_ ));
-DESCR("less-than-or-equal");
-DATA(insert OID = 1073 (  varchargt		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varchargt - _null_ ));
-DESCR("greater-than");
-DATA(insert OID = 1074 (  varcharge		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varcharge - _null_ ));
-DESCR("greater-than-or-equal");
-DATA(insert OID = 1075 (  varcharne		   PGNSP PGUID 12 f f t f i 2 16 "1043 1043"	varcharne - _null_ ));
-DESCR("not equal");
 DATA(insert OID = 1078 (  bpcharcmp		   PGNSP PGUID 12 f f t f i 2 23 "1042 1042"	bpcharcmp - _null_ ));
 DESCR("less-equal-greater");
-DATA(insert OID = 1079 (  varcharcmp	   PGNSP PGUID 12 f f t f i 2 23 "1043 1043"	varcharcmp - _null_ ));
-DESCR("less-equal-greater");
 DATA(insert OID = 1080 (  hashbpchar	   PGNSP PGUID 12 f f t f i 1 23 "1042"  hashbpchar - _null_ ));
 DESCR("hash");
 DATA(insert OID = 1081 (  format_type	   PGNSP PGUID 12 f f f f s 2 25 "26 23" format_type - _null_ ));
@@ -1488,7 +1476,7 @@ DESCR("minus");
 DATA(insert OID = 1191 (  timestamptz		PGNSP PGUID 12 f f t f s 1 1184 "25"	text_timestamptz - _null_ ));
 DESCR("convert text to timestamp with time zone");
 DATA(insert OID = 1192 (  text				PGNSP PGUID 12 f f t f s 1	 25 "1184"	timestamptz_text - _null_ ));
-DESCR("convert timestamp to text");
+DESCR("convert timestamp with time zone to text");
 DATA(insert OID = 1193 (  text				PGNSP PGUID 12 f f t f i 1	 25 "1186"	interval_text - _null_ ));
 DESCR("convert interval to text");
 DATA(insert OID = 1194 (  reltime			PGNSP PGUID 12 f f t f i 1	703 "1186"	interval_reltime - _null_ ));
@@ -1594,7 +1582,7 @@ DESCR("convert time and date to timestamp");
 DATA(insert OID = 1297 (  datetimetz_pl    PGNSP PGUID 12 f f t f i 2 1184 "1082 1266"	datetimetz_timestamptz - _null_ ));
 DESCR("convert date and time with time zone to timestamp with time zone");
 DATA(insert OID = 1298 (  timetzdate_pl    PGNSP PGUID 14 f f t f i 2 1184 "1266 1082"	"select ($2 + $1)" - _null_ ));
-DESCR("convert time with time zone and date to timestamp");
+DESCR("convert time with time zone and date to timestamp with time zone");
 DATA(insert OID = 1299 (  now			   PGNSP PGUID 12 f f t f s 0 1184 ""  now - _null_ ));
 DESCR("current transaction time");
 
@@ -1642,8 +1630,9 @@ DATA(insert OID = 1317 (  length			 PGNSP PGUID 12 f f t f i 1 23 "25"  textlen
 DESCR("length");
 DATA(insert OID = 1318 (  length			 PGNSP PGUID 12 f f t f i 1 23 "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1319 (  length			 PGNSP PGUID 12 f f t f i 1 23 "1043"  varcharlen - _null_ ));
-DESCR("character length");
+
+DATA(insert OID = 1319 (  xideqint4			 PGNSP PGUID 12 f f t f i 2 16 "28 23"	xideq - _null_ ));
+DESCR("equal");
 
 DATA(insert OID = 1326 (  interval_div		 PGNSP PGUID 12 f f t f i 2 1186 "1186 701"  interval_div - _null_ ));
 DESCR("divide");
@@ -1703,8 +1692,6 @@ DESCR("convert abstime to time");
 
 DATA(insert OID = 1367 (  character_length	PGNSP PGUID 12 f f t f i 1	23 "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1368 (  character_length	PGNSP PGUID 12 f f t f i 1	23 "1043"  varcharlen - _null_ ));
-DESCR("character length");
 DATA(insert OID = 1369 (  character_length	PGNSP PGUID 12 f f t f i 1	23 "25"  textlen - _null_ ));
 DESCR("character length");
 
@@ -1712,15 +1699,11 @@ DATA(insert OID = 1370 (  interval			 PGNSP PGUID 12 f f t f i 1 1186 "1083"  ti
 DESCR("convert time to interval");
 DATA(insert OID = 1372 (  char_length		 PGNSP PGUID 12 f f t f i 1 23	 "1042"  bpcharlen - _null_ ));
 DESCR("character length");
-DATA(insert OID = 1373 (  char_length		 PGNSP PGUID 12 f f t f i 1 23	 "1043"  varcharlen - _null_ ));
-DESCR("character length");
 
 DATA(insert OID = 1374 (  octet_length			 PGNSP PGUID 12 f f t f i 1 23	 "25"  textoctetlen - _null_ ));
 DESCR("octet length");
 DATA(insert OID = 1375 (  octet_length			 PGNSP PGUID 12 f f t f i 1 23	 "1042"  bpcharoctetlen - _null_ ));
 DESCR("octet length");
-DATA(insert OID = 1376 (  octet_length			 PGNSP PGUID 12 f f t f i 1 23	 "1043"  varcharoctetlen - _null_ ));
-DESCR("octet length");
 
 DATA(insert OID = 1377 (  time_larger	   PGNSP PGUID 12 f f t f i 2 1083 "1083 1083"	time_larger - _null_ ));
 DESCR("larger of two");
@@ -2106,6 +2089,11 @@ DESCR("convert SQL99 regexp pattern to POSIX style");
 
 DATA(insert OID = 1624 (  mul_d_interval	PGNSP PGUID 12 f f t f i 2 1186 "701 1186"	mul_d_interval - _null_ ));
 
+DATA(insert OID = 1631 (  bpcharlike	   PGNSP PGUID 12 f f t f i 2 16 "1042 25" textlike - _null_ ));
+DESCR("matches LIKE expression");
+DATA(insert OID = 1632 (  bpcharnlike	   PGNSP PGUID 12 f f t f i 2 16 "1042 25" textnlike - _null_ ));
+DESCR("does not match LIKE expression");
+
 DATA(insert OID = 1633 (  texticlike		PGNSP PGUID 12 f f t f i 2 16 "25 25" texticlike - _null_ ));
 DESCR("matches LIKE expression, case-insensitive");
 DATA(insert OID = 1634 (  texticnlike		PGNSP PGUID 12 f f t f i 2 16 "25 25" texticnlike - _null_ ));
@@ -2117,6 +2105,19 @@ DESCR("does not match LIKE expression, case-insensitive");
 DATA(insert OID = 1637 (  like_escape		PGNSP PGUID 12 f f t f i 2 25 "25 25" like_escape - _null_ ));
 DESCR("convert LIKE pattern to use backslash escapes");
 
+DATA(insert OID = 1656 (  bpcharicregexeq    PGNSP PGUID 12 f f t f i 2 16 "1042 25"	texticregexeq - _null_ ));
+DESCR("matches regex., case-insensitive");
+DATA(insert OID = 1657 (  bpcharicregexne    PGNSP PGUID 12 f f t f i 2 16 "1042 25"	texticregexne - _null_ ));
+DESCR("does not match regex., case-insensitive");
+DATA(insert OID = 1658 (  bpcharregexeq	   PGNSP PGUID 12 f f t f i 2 16 "1042 25"	textregexeq - _null_ ));
+DESCR("matches regex., case-sensitive");
+DATA(insert OID = 1659 (  bpcharregexne	   PGNSP PGUID 12 f f t f i 2 16 "1042 25"	textregexne - _null_ ));
+DESCR("does not match regex., case-sensitive");
+DATA(insert OID = 1660 (  bpchariclike		PGNSP PGUID 12 f f t f i 2 16 "1042 25" texticlike - _null_ ));
+DESCR("matches LIKE expression, case-insensitive");
+DATA(insert OID = 1661 (  bpcharicnlike		PGNSP PGUID 12 f f t f i 2 16 "1042 25" texticnlike - _null_ ));
+DESCR("does not match LIKE expression, case-insensitive");
+
 DATA(insert OID = 1689 (  update_pg_pwd_and_pg_group  PGNSP PGUID 12 f f t f v 0 2279  ""	update_pg_pwd_and_pg_group - _null_ ));
 DESCR("update pg_pwd and pg_group files");
 
@@ -2532,7 +2533,7 @@ DESCR("format float8 to text");
 DATA(insert OID = 1777 ( to_number			PGNSP PGUID 12 f f t f i 2	1700 "25 25"  numeric_to_number - _null_ ));
 DESCR("convert text to numeric");
 DATA(insert OID = 1778 ( to_timestamp		PGNSP PGUID 12 f f t f s 2	1184 "25 25"  to_timestamp - _null_ ));
-DESCR("convert text to timestamp");
+DESCR("convert text to timestamp with time zone");
 DATA(insert OID = 1780 ( to_date			PGNSP PGUID 12 f f t f i 2	1082 "25 25"  to_date - _null_ ));
 DESCR("convert text to date");
 DATA(insert OID = 1768 ( to_char			PGNSP PGUID 12 f f t f i 2	25 "1186 25"  interval_to_char - _null_ ));
@@ -2803,7 +2804,7 @@ DATA(insert OID = 1966 (  oidsmaller	   PGNSP PGUID 12 f f t f i 2 26 "26 26"	oi
 DESCR("smaller of two");
 
 DATA(insert OID = 1967 (  timestamptz	   PGNSP PGUID 12 f f t f i 2 1184 "1184 23"	timestamptz_scale - _null_ ));
-DESCR("adjust timestamp precision");
+DESCR("adjust timestamptz precision");
 DATA(insert OID = 1968 (  time			   PGNSP PGUID 12 f f t f i 2 1083 "1083 23"	time_scale - _null_ ));
 DESCR("adjust time precision");
 DATA(insert OID = 1969 (  timetz		   PGNSP PGUID 12 f f t f i 2 1266 "1266 23"	timetz_scale - _null_ ));
@@ -2851,9 +2852,9 @@ DESCR("convert date to timestamp");
 DATA(insert OID = 2025 (  timestamp			PGNSP PGUID 12 f f t f i 2 1114 "1082 1083"  datetime_timestamp - _null_ ));
 DESCR("convert date and time to timestamp");
 DATA(insert OID = 2027 (  timestamp			PGNSP PGUID 12 f f t f s 1 1114 "1184"	timestamptz_timestamp - _null_ ));
-DESCR("convert date and time with time zone to timestamp");
+DESCR("convert timestamp with time zone to timestamp");
 DATA(insert OID = 2028 (  timestamptz		PGNSP PGUID 12 f f t f s 1 1184 "1114"	timestamp_timestamptz - _null_ ));
-DESCR("convert date and time with time zone to timestamp");
+DESCR("convert timestamp to timestamp with time zone");
 DATA(insert OID = 2029 (  date				PGNSP PGUID 12 f f t f i 1 1082 "1114"	timestamp_date - _null_ ));
 DESCR("convert timestamp to date");
 DATA(insert OID = 2030 (  abstime			PGNSP PGUID 12 f f t f s 1	702 "1114"	timestamp_abstime - _null_ ));
@@ -3033,14 +3034,6 @@ DATA(insert OID = 2165 ( text_pattern_ne     PGNSP PGUID 12 f f t f i 2 16 "25 2
 DATA(insert OID = 2166 ( bttext_pattern_cmp  PGNSP PGUID 12 f f t f i 2 23 "25 25" bttext_pattern_cmp - _null_ ));
 
 /* We use the same procedures here as above since the types are binary compatible. */
-DATA(insert OID = 2167 ( varchar_pattern_lt    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_lt - _null_ ));
-DATA(insert OID = 2168 ( varchar_pattern_le    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_le - _null_ ));
-DATA(insert OID = 2169 ( varchar_pattern_eq    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_eq - _null_ ));
-DATA(insert OID = 2170 ( varchar_pattern_ge    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_ge - _null_ ));
-DATA(insert OID = 2171 ( varchar_pattern_gt    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_gt - _null_ ));
-DATA(insert OID = 2172 ( varchar_pattern_ne    PGNSP PGUID 12 f f t f i 2 16 "1043 1043" text_pattern_ne - _null_ ));
-DATA(insert OID = 2173 ( btvarchar_pattern_cmp PGNSP PGUID 12 f f t f i 2 23 "1043 1043" bttext_pattern_cmp - _null_ ));
-
 DATA(insert OID = 2174 ( bpchar_pattern_lt    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_lt - _null_ ));
 DATA(insert OID = 2175 ( bpchar_pattern_le    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_le - _null_ ));
 DATA(insert OID = 2176 ( bpchar_pattern_eq    PGNSP PGUID 12 f f t f i 2 16 "1042 1042" text_pattern_eq - _null_ ));
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 768e493e0c9c0be86ff7d63169519d8beebc7b6d..b36e17f91d7801989f190edc7977f0bf815a7329 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.66 2003/02/15 20:12:41 tgl Exp $
+ * $Id: paths.h,v 1.67 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,8 @@ extern Path *best_inner_indexscan(Query *root, RelOptInfo *rel,
 extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
 								IndexOptInfo *index,
 								Expr *orsubclause);
-extern List *expand_indexqual_conditions(List *indexquals);
+extern List *expand_indexqual_conditions(IndexOptInfo *index,
+										 List *clausegroups);
 
 /*
  * orindxpath.c
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 2b1a1fad4b9734f74ff9008efbc89c74a63e53cd..5f2adf5c4a1d9b15c3239878a46a9cdd6e84923e 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -7,13 +7,14 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.45 2003/04/29 22:13:11 tgl Exp $
+ * $Id: parse_func.h,v 1.46 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PARSER_FUNC_H
 #define PARSER_FUNC_H
 
+#include "catalog/namespace.h"
 #include "parser/parse_node.h"
 
 
@@ -48,6 +49,15 @@ extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
 				Oid *funcid, Oid *rettype,
 				bool *retset, Oid **true_typeids);
 
+extern int	func_match_argtypes(int nargs,
+								Oid *input_typeids,
+								FuncCandidateList raw_candidates,
+								FuncCandidateList *candidates);
+
+extern FuncCandidateList func_select_candidate(int nargs,
+											   Oid *input_typeids,
+											   FuncCandidateList candidates);
+
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
 
 extern void make_fn_arguments(ParseState *pstate,
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 539dfbc9de79aea7010f23a79800b2d6554cfc16..42e9a84c77b8bf34f5046e15e8c8954e8a19d75e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.218 2003/05/23 22:33:23 tgl Exp $
+ * $Id: builtins.h,v 1.219 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -496,15 +496,6 @@ extern Datum varcharout(PG_FUNCTION_ARGS);
 extern Datum varcharrecv(PG_FUNCTION_ARGS);
 extern Datum varcharsend(PG_FUNCTION_ARGS);
 extern Datum varchar(PG_FUNCTION_ARGS);
-extern Datum varchareq(PG_FUNCTION_ARGS);
-extern Datum varcharne(PG_FUNCTION_ARGS);
-extern Datum varcharlt(PG_FUNCTION_ARGS);
-extern Datum varcharle(PG_FUNCTION_ARGS);
-extern Datum varchargt(PG_FUNCTION_ARGS);
-extern Datum varcharge(PG_FUNCTION_ARGS);
-extern Datum varcharcmp(PG_FUNCTION_ARGS);
-extern Datum varcharlen(PG_FUNCTION_ARGS);
-extern Datum varcharoctetlen(PG_FUNCTION_ARGS);
 
 /* varlena.c */
 extern Datum textin(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 848cc9f146e9c4415c2939ec62ee29d790fa8019..66b497a98b14fd3c975f61cd2145f3a9d08a2b69 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.69 2003/05/09 18:08:48 tgl Exp $
+ * $Id: lsyscache.h,v 1.70 2003/05/26 00:11:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 extern bool op_in_opclass(Oid opno, Oid opclass);
 extern bool op_requires_recheck(Oid opno, Oid opclass);
+extern Oid	get_opclass_member(Oid opclass, int16 strategy);
 extern char *get_attname(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
 extern Oid	get_atttype(Oid relid, AttrNumber attnum);
diff --git a/src/test/regress/expected/name.out b/src/test/regress/expected/name.out
index 8cc09dae7442514f465712393c0b17a93e485151..f6c3a17fe51f659d98a572df3913545dfc6aedec 100644
--- a/src/test/regress/expected/name.out
+++ b/src/test/regress/expected/name.out
@@ -27,7 +27,7 @@ INSERT INTO NAME_TBL(f1) VALUES ('d34aaasdf');
 INSERT INTO NAME_TBL(f1) VALUES ('');
 INSERT INTO NAME_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ');
 SELECT '' AS seven, NAME_TBL.*;
- seven |               f1                
+ seven |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -39,7 +39,7 @@ SELECT '' AS seven, NAME_TBL.*;
 (7 rows)
 
 SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- six |               f1                
+ six |                               f1                                
 -----+-----------------------------------------------------------------
      | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
      | asdfghjkl;
@@ -49,20 +49,20 @@ SELECT '' AS six, c.f1 FROM NAME_TBL c WHERE c.f1 <> '1234567890ABCDEFGHIJKLMNOP
 (5 rows)
 
 SELECT '' AS one, c.f1 FROM NAME_TBL c WHERE c.f1 = '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- one |        f1        
+ one |                               f1                                
 -----+-----------------------------------------------------------------
      | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
      | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
 (2 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 < '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- three |               f1                
+ three | f1 
 -------+----
        | 
 (1 row)
 
 SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- four |               f1                
+ four |                               f1                                
 ------+-----------------------------------------------------------------
       | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
       | 
@@ -70,7 +70,7 @@ SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 <= '1234567890ABCDEFGHIJKLMNO
 (3 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- three |        f1        
+ three |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
        | asdfghjkl;
@@ -79,7 +79,7 @@ SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 > '1234567890ABCDEFGHIJKLMNO
 (4 rows)
 
 SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQR';
- four |        f1        
+ four |                               f1                                
 ------+-----------------------------------------------------------------
       | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
       | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -90,7 +90,7 @@ SELECT '' AS four, c.f1 FROM NAME_TBL c WHERE c.f1 >= '1234567890ABCDEFGHIJKLMNO
 (6 rows)
 
 SELECT '' AS seven, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '.*';
- seven |               f1                
+ seven |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
@@ -107,7 +107,7 @@ SELECT '' AS zero, c.f1 FROM NAME_TBL c WHERE c.f1 !~ '.*';
 (0 rows)
 
 SELECT '' AS three, c.f1 FROM NAME_TBL c WHERE c.f1 ~ '[0-9]';
- three |               f1                
+ three |                               f1                                
 -------+-----------------------------------------------------------------
        | 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890ABCDEFGHIJKLMNOPQ
        | 1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopq
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 2573d2011c3bbcea454b8402d6468ab927247f33..e0504706f3c1c2a5f50d465cb13199b3247a99d1 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -14,6 +14,23 @@
 --
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
+-- Helper functions to deal with cases where binary-coercible matches are
+-- allowed.
+-- This should match IsBinaryCoercible() in parse_coerce.c.
+create function binary_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0 and castcontext = ''i'')'
+language sql;
+-- This one ignores castcontext, so it considers only physical equivalence
+-- and not whether the coercion can be invoked implicitly.
+create function physically_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0)'
+language sql;
 -- **************** pg_proc ****************
 -- Look for illegal values in pg_proc fields.
 -- NOTE: in reality pronargs could be more than 10, but I'm too lazy to put
@@ -105,11 +122,10 @@ WHERE p1.oid != p2.oid AND
 -------------+-------------
           25 |        1042
           25 |        1043
-        1042 |        1043
         1114 |        1184
         1560 |        1562
         2277 |        2283
-(6 rows)
+(5 rows)
 
 SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
 FROM pg_proc AS p1, pg_proc AS p2
@@ -120,13 +136,12 @@ WHERE p1.oid != p2.oid AND
     (p1.proargtypes[1] < p2.proargtypes[1]);
  proargtypes | proargtypes 
 -------------+-------------
+          23 |          28
           25 |        1042
-          25 |        1043
-        1042 |        1043
         1114 |        1184
         1560 |        1562
         2277 |        2283
-(6 rows)
+(5 rows)
 
 SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
 FROM pg_proc AS p1, pg_proc AS p2
@@ -228,23 +243,17 @@ SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT (c.castsource = p.proargtypes[0] OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = c.castsource AND
-                       k.casttarget = p.proargtypes[0]))
-     OR NOT (p.prorettype = c.casttarget OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = p.prorettype AND
-                       k.casttarget = c.casttarget)));
+     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT binary_coercible(p.prorettype, c.casttarget));
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
 (0 rows)
 
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
--- also binary compatible.  This is legal, but probably not intended.
+-- also binary compatible.  This is legal, but usually not intended.
+-- As of 7.4, this finds the casts from text and varchar to bpchar, because
+-- those are binary-compatible while the reverse way goes through rtrim().
 SELECT *
 FROM pg_cast c
 WHERE c.castfunc = 0 AND
@@ -254,7 +263,9 @@ WHERE c.castfunc = 0 AND
                     k.casttarget = c.castsource);
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
-(0 rows)
+         25 |       1042 |        0 | i
+       1043 |       1042 |        0 | i
+(2 rows)
 
 -- **************** pg_operator ****************
 -- Look for illegal values in pg_operator fields.
@@ -425,14 +436,15 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 -- Hashing only works on simple equality operators "type = sametype",
 -- since the hash itself depends on the bitwise representation of the type.
 -- Check that allegedly hashable operators look like they might be "=".
--- NOTE: in 7.3, this search finds xideqint4.
--- Until we have some cleaner way of dealing with binary-equivalent types,
--- just leave that tuple in the expected output.
+-- NOTE: as of 7.3, this search finds xideqint4.  Since we do not mark
+-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
+-- recognize that case as OK; just leave that tuple in the expected output.
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
     (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
-     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND p1.oprcom = p1.oid);
+     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND
+     p1.oprcom = p1.oid);
  oid | oprname 
 -----+---------
  353 | =
@@ -464,33 +476,26 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
 
 -- Check that each operator defined in pg_operator matches its oprcode entry
 -- in pg_proc.  Easiest to do this separately for each oprkind.
--- FIXME: want to check that argument/result types match, but how to do that
--- in the face of binary-compatible types?
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'b' AND
     (p2.pronargs != 2
--- diked out until we find a way of marking binary-compatible types
--- OR
---     p1.oprresult != p2.prorettype OR
---     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
---     (p1.oprright != p2.proargtypes[1] AND p2.proargtypes[1] != 0)
-);
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[1]));
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
 
--- These two selects can be left as-is because there are no binary-compatible
--- cases that they trip over, at least in 6.5:
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'l' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprright != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprleft != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[0])
+     OR p1.oprleft != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -499,10 +504,10 @@ SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'r' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprright != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR p1.oprright != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -591,48 +596,46 @@ WHERE a.aggfnoid = p.oid AND
 (0 rows)
 
 -- Cross-check transfn against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
--- NOTE: in 7.1, this search finds max and min on abstime, which are
--- implemented using int4larger/int4smaller.  Until we have
--- some cleaner way of dealing with binary-equivalent types, just leave
--- those two tuples in the expected output.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+-- NOTE: use physically_coercible here, not binary_coercible, because
+-- max and min on abstime are implemented using int4larger/int4smaller.
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND
-    (p2.proretset OR
-     a.aggtranstype != p2.prorettype OR
-     a.aggtranstype != p2.proargtypes[0] OR
-     NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
-ORDER BY 1;
- aggfnoid | proname | oid |   proname   
-----------+---------+-----+-------------
-     2121 | max     | 768 | int4larger
-     2137 | min     | 769 | int4smaller
-(2 rows)
+    a.aggtransfn = ptr.oid AND
+    (ptr.proretset
+     OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
+     OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
+     OR NOT ((ptr.pronargs = 2 AND
+              physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
+             OR
+             (ptr.pronargs = 1 AND
+              p.proargtypes[0] = '"any"'::regtype)));
+ aggfnoid | proname | oid | proname 
+----------+---------+-----+---------
+(0 rows)
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn
 WHERE a.aggfnoid = p.oid AND
-    a.aggfinalfn = p2.oid AND
-    (p2.proretset OR p.prorettype != p2.prorettype OR
-     p2.pronargs != 1 OR
-     a.aggtranstype != p2.proargtypes[0]);
+    a.aggfinalfn = pfn.oid AND
+    (pfn.proretset
+     OR NOT binary_coercible(pfn.prorettype, p.prorettype)
+     OR pfn.pronargs != 1
+     OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
  aggfnoid | proname | oid | proname 
 ----------+---------+-----+---------
 (0 rows)
 
 -- If transfn is strict then either initval should be non-NULL, or
--- input type should equal transtype so that the first non-null input
+-- input type should match transtype so that the first non-null input
 -- can be assigned as the state value.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND p2.proisstrict AND
-    a.agginitval IS NULL AND p.proargtypes[0] != a.aggtranstype;
+    a.aggtransfn = ptr.oid AND ptr.proisstrict AND
+    a.agginitval IS NULL AND
+    NOT binary_coercible(p.proargtypes[0], a.aggtranstype);
  aggfnoid | proname | oid | proname 
 ----------+---------+-----+---------
 (0 rows)
@@ -714,7 +717,8 @@ WHERE p1.amopopr = p2.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (p3.opcintype != p2.oprleft OR p3.opcintype != p2.oprright);
+    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
+     p2.oprleft != p2.oprright);
  amopclaid | amopopr | oid | oprname | opcname 
 -----------+---------+-----+---------+---------
 (0 rows)
@@ -752,7 +756,8 @@ WHERE p2.opcamid = p1.oid AND
 -- signature of the function may be different for different support routines
 -- or different base data types.
 -- We can check that all the referenced instances of the same support
--- routine number take the same number of parameters, but that's about it...
+-- routine number take the same number of parameters, but that's about it
+-- for a general check...
 SELECT p1.amopclaid, p1.amprocnum,
 	p2.oid, p2.proname,
 	p3.opcname,
@@ -769,3 +774,22 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
 -----------+-----------+-----+---------+---------+-----------+-----------+-----+---------+---------
 (0 rows)
 
+-- For btree, though, we can do better since we know the support routines
+-- must be of the form cmp(input, input) returns int4.
+SELECT p1.amopclaid, p1.amprocnum,
+	p2.oid, p2.proname,
+	p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[0] != proargtypes[1]);
+ amopclaid | amprocnum | oid | proname | opcname 
+-----------+-----------+-----+---------+---------
+(0 rows)
+
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index f173b6cf5440cb40e50e6ae56613ad1c9bc967dc..322b84ff100b4a4ff16b204dfd5290e154cde4d9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1317,9 +1317,9 @@ SELECT tablename, rulename, definition FROM pg_rules
 ---------------+-----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  pg_settings   | pg_settings_n   | CREATE RULE pg_settings_n AS ON UPDATE TO pg_settings DO INSTEAD NOTHING;
  pg_settings   | pg_settings_u   | CREATE RULE pg_settings_u AS ON UPDATE TO pg_settings WHERE (new.name = old.name) DO SELECT set_config(old.name, new.setting, false) AS set_config;
- rtest_emp     | rtest_emp_del   | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired     '::bpchar, '$0.00'::money, old.salary);
- rtest_emp     | rtest_emp_ins   | CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'hired     '::bpchar, new.salary, '$0.00'::money);
- rtest_emp     | rtest_emp_upd   | CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'honored   '::bpchar, new.salary, old.salary);
+ rtest_emp     | rtest_emp_del   | CREATE RULE rtest_emp_del AS ON DELETE TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (old.ename, "current_user"(), 'fired'::bpchar, '$0.00'::money, old.salary);
+ rtest_emp     | rtest_emp_ins   | CREATE RULE rtest_emp_ins AS ON INSERT TO rtest_emp DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'hired'::bpchar, new.salary, '$0.00'::money);
+ rtest_emp     | rtest_emp_upd   | CREATE RULE rtest_emp_upd AS ON UPDATE TO rtest_emp WHERE (new.salary <> old.salary) DO INSERT INTO rtest_emplog (ename, who, "action", newsal, oldsal) VALUES (new.ename, "current_user"(), 'honored'::bpchar, new.salary, old.salary);
  rtest_nothn1  | rtest_nothn_r1  | CREATE RULE rtest_nothn_r1 AS ON INSERT TO rtest_nothn1 WHERE ((new.a >= 10) AND (new.a < 20)) DO INSTEAD NOTHING;
  rtest_nothn1  | rtest_nothn_r2  | CREATE RULE rtest_nothn_r2 AS ON INSERT TO rtest_nothn1 WHERE ((new.a >= 30) AND (new.a < 40)) DO INSTEAD NOTHING;
  rtest_nothn2  | rtest_nothn_r3  | CREATE RULE rtest_nothn_r3 AS ON INSERT TO rtest_nothn2 WHERE (new.a >= 100) DO INSTEAD INSERT INTO rtest_nothn3 (a, b) VALUES (new.a, new.b);
diff --git a/src/test/regress/expected/select_having.out b/src/test/regress/expected/select_having.out
index 4447a9df4d6f804da027e560284ac15b4b09fabc..37793d49b57a47dce0625f9b9a1f2ddf247c1ae2 100644
--- a/src/test/regress/expected/select_having.out
+++ b/src/test/regress/expected/select_having.out
@@ -33,11 +33,11 @@ SELECT b, c FROM test_having
 SELECT lower(c), count(c) FROM test_having
 	GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a)
 	ORDER BY lower(c);
-  lower   | count 
-----------+-------
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
+ lower | count 
+-------+-------
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (3 rows)
 
 SELECT c, max(a) FROM test_having
diff --git a/src/test/regress/expected/select_having_1.out b/src/test/regress/expected/select_having_1.out
index ffbb591b12533c8a77381c2658b5e894d9cf5f17..6154bcbfe86e42d3dac1794e9d3fd5df47b51531 100644
--- a/src/test/regress/expected/select_having_1.out
+++ b/src/test/regress/expected/select_having_1.out
@@ -33,11 +33,11 @@ SELECT b, c FROM test_having
 SELECT lower(c), count(c) FROM test_having
 	GROUP BY lower(c) HAVING count(*) > 2 OR min(a) = max(a)
 	ORDER BY lower(c);
-  lower   | count 
-----------+-------
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
+ lower | count 
+-------+-------
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (3 rows)
 
 SELECT c, max(a) FROM test_having
diff --git a/src/test/regress/expected/select_implicit.out b/src/test/regress/expected/select_implicit.out
index 47d2e80289eb77db45c00a7047ba41c5ffc7c387..d8a9edbd7810babff70a5ccb9b893b8f0ff8d5b4 100644
--- a/src/test/regress/expected/select_implicit.out
+++ b/src/test/regress/expected/select_implicit.out
@@ -248,12 +248,12 @@ SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2;
 --   w/ existing GROUP BY target using a relation name in target
 SELECT lower(test_missing_target.c), count(c)
   FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c);
-  lower   | count 
-----------+-------
- aaaa     |     2
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
+ lower | count 
+-------+-------
+ aaaa  |     2
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (4 rows)
 
 --   w/o existing GROUP BY target
diff --git a/src/test/regress/expected/select_implicit_1.out b/src/test/regress/expected/select_implicit_1.out
index 80abfd8f9a271face02cdc35b35bedbfeffa57ee..fa67bf3a5d126a33ef9a0f8e3a50c6f1f32e189d 100644
--- a/src/test/regress/expected/select_implicit_1.out
+++ b/src/test/regress/expected/select_implicit_1.out
@@ -248,12 +248,12 @@ SELECT count(b) FROM test_missing_target GROUP BY b/2 ORDER BY b/2;
 --   w/ existing GROUP BY target using a relation name in target
 SELECT lower(test_missing_target.c), count(c)
   FROM test_missing_target GROUP BY lower(c) ORDER BY lower(c);
-  lower   | count 
-----------+-------
- aaaa     |     2
- bbbb     |     3
- cccc     |     4
- xxxx     |     1
+ lower | count 
+-------+-------
+ aaaa  |     2
+ bbbb  |     3
+ cccc  |     4
+ xxxx  |     1
 (4 rows)
 
 --   w/o existing GROUP BY target
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index bb999785eb12b58c2edfe21aabaeef58d0b334a4..26c18d1690b6396847ea43228d065b58511534bc 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -26,8 +26,8 @@ ERROR:  parser: parse error at or near "' - third line'" at character 75
 SELECT CAST(f1 AS text) AS "text(char)" FROM CHAR_TBL;
  text(char) 
 ------------
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (4 rows)
@@ -88,8 +88,8 @@ SELECT CAST(f1 AS varchar) AS "varchar(text)" FROM TEXT_TBL;
 SELECT CAST(f1 AS varchar) AS "varchar(char)" FROM CHAR_TBL;
  varchar(char) 
 ---------------
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (4 rows)
@@ -570,16 +570,16 @@ SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
  text and unknown
 (1 row)
 
-SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
- Concat char to unknown type  
-------------------------------
- characters          and text
+SELECT char(20) 'characters' || ' and text' AS "Concat char to unknown type";
+ Concat char to unknown type 
+-----------------------------
+ characters and text
 (1 row)
 
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
-   Concat text to char    
---------------------------
- text and characters     
+ Concat text to char 
+---------------------
+ text and characters
 (1 row)
 
 SELECT text 'text' || varchar ' and varchar' AS "Concat text to varchar";
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index a46a44becab4b2a76792d5871373fe02aeea051e..43d32298a58ff539eb66fd51651914342a805fbe 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -203,21 +203,19 @@ SELECT f1 FROM INT4_TBL
                 123456
 (5 rows)
 
-SELECT f1 AS five FROM VARCHAR_TBL
+SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL
 UNION
 SELECT f1 FROM CHAR_TBL;
- five 
-------
- a
+ three 
+-------
  a   
- ab
  ab  
  abcd
-(5 rows)
+(3 rows)
 
 SELECT f1 AS three FROM VARCHAR_TBL
 UNION
-SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL;
+SELECT CAST(f1 AS varchar) FROM CHAR_TBL;
  three 
 -------
  a
@@ -234,8 +232,8 @@ SELECT f1 FROM CHAR_TBL;
  ab
  abcd
  abcd
- a   
- ab  
+ a
+ ab
  abcd
  abcd
 (8 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 5be76aa3b41fbf1f00e13134ed2bda1643f2805c..4b7bd7b4dd5c7b100cbe7d2aa7eaf5049e4ec226 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -15,6 +15,27 @@
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
 
+
+-- Helper functions to deal with cases where binary-coercible matches are
+-- allowed.
+
+-- This should match IsBinaryCoercible() in parse_coerce.c.
+create function binary_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0 and castcontext = ''i'')'
+language sql;
+
+-- This one ignores castcontext, so it considers only physical equivalence
+-- and not whether the coercion can be invoked implicitly.
+create function physically_coercible(oid, oid) returns bool as
+'SELECT ($1 = $2) OR
+ EXISTS(select 1 from pg_cast where
+        castsource = $1 and casttarget = $2 and
+        castfunc = 0)'
+language sql;
+
 -- **************** pg_proc ****************
 
 -- Look for illegal values in pg_proc fields.
@@ -180,20 +201,15 @@ SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
     (p.pronargs <> 1
-     OR NOT (c.castsource = p.proargtypes[0] OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = c.castsource AND
-                       k.casttarget = p.proargtypes[0]))
-     OR NOT (p.prorettype = c.casttarget OR
-             EXISTS (SELECT 1 FROM pg_cast k
-                     WHERE k.castfunc = 0 AND
-                       k.castsource = p.prorettype AND
-                       k.casttarget = c.casttarget)));
+     OR NOT binary_coercible(c.castsource, p.proargtypes[0])
+     OR NOT binary_coercible(p.prorettype, c.casttarget));
 
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
--- also binary compatible.  This is legal, but probably not intended.
+-- also binary compatible.  This is legal, but usually not intended.
+
+-- As of 7.4, this finds the casts from text and varchar to bpchar, because
+-- those are binary-compatible while the reverse way goes through rtrim().
 
 SELECT *
 FROM pg_cast c
@@ -347,15 +363,17 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 -- Hashing only works on simple equality operators "type = sametype",
 -- since the hash itself depends on the bitwise representation of the type.
 -- Check that allegedly hashable operators look like they might be "=".
--- NOTE: in 7.3, this search finds xideqint4.
--- Until we have some cleaner way of dealing with binary-equivalent types,
--- just leave that tuple in the expected output.
+
+-- NOTE: as of 7.3, this search finds xideqint4.  Since we do not mark
+-- xid and int4 as binary-equivalent in pg_cast, there's no easy way to
+-- recognize that case as OK; just leave that tuple in the expected output.
 
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
     (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
-     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND p1.oprcom = p1.oid);
+     p1.oprleft = p1.oprright AND p1.oprname IN ('=', '~=~') AND
+     p1.oprcom = p1.oid);
 
 -- In 6.5 we accepted hashable array equality operators when the array element
 -- type is hashable.  However, what we actually need to make hashjoin work on
@@ -382,41 +400,33 @@ WHERE p1.oprcanhash AND p1.oprcode = p2.oid AND p2.proname = 'array_eq';
 
 -- Check that each operator defined in pg_operator matches its oprcode entry
 -- in pg_proc.  Easiest to do this separately for each oprkind.
--- FIXME: want to check that argument/result types match, but how to do that
--- in the face of binary-compatible types?
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'b' AND
     (p2.pronargs != 2
--- diked out until we find a way of marking binary-compatible types
--- OR
---     p1.oprresult != p2.prorettype OR
---     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
---     (p1.oprright != p2.proargtypes[1] AND p2.proargtypes[1] != 0)
-);
-
--- These two selects can be left as-is because there are no binary-compatible
--- cases that they trip over, at least in 6.5:
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[1]));
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'l' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprright != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprleft != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprright, p2.proargtypes[0])
+     OR p1.oprleft != 0);
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprcode = p2.oid AND
     p1.oprkind = 'r' AND
-    (p2.pronargs != 1 OR
-     p1.oprresult != p2.prorettype OR
-     (p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
-     p1.oprright != 0);
+    (p2.pronargs != 1
+     OR NOT binary_coercible(p2.prorettype, p1.oprresult)
+     OR NOT binary_coercible(p1.oprleft, p2.proargtypes[0])
+     OR p1.oprright != 0);
 
 -- If the operator is mergejoinable or hashjoinable, its underlying function
 -- should not be volatile.
@@ -489,42 +499,42 @@ WHERE a.aggfnoid = p.oid AND
     a.aggfinalfn = 0 AND p.prorettype != a.aggtranstype;
 
 -- Cross-check transfn against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
--- NOTE: in 7.1, this search finds max and min on abstime, which are
--- implemented using int4larger/int4smaller.  Until we have
--- some cleaner way of dealing with binary-equivalent types, just leave
--- those two tuples in the expected output.
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+-- NOTE: use physically_coercible here, not binary_coercible, because
+-- max and min on abstime are implemented using int4larger/int4smaller.
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND
-    (p2.proretset OR
-     a.aggtranstype != p2.prorettype OR
-     a.aggtranstype != p2.proargtypes[0] OR
-     NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
-          (p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
-ORDER BY 1;
+    a.aggtransfn = ptr.oid AND
+    (ptr.proretset
+     OR NOT physically_coercible(ptr.prorettype, a.aggtranstype)
+     OR NOT physically_coercible(a.aggtranstype, ptr.proargtypes[0])
+     OR NOT ((ptr.pronargs = 2 AND
+              physically_coercible(p.proargtypes[0], ptr.proargtypes[1]))
+             OR
+             (ptr.pronargs = 1 AND
+              p.proargtypes[0] = '"any"'::regtype)));
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
--- FIXME: what about binary-compatible types?
 
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, pfn.oid, pfn.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS pfn
 WHERE a.aggfnoid = p.oid AND
-    a.aggfinalfn = p2.oid AND
-    (p2.proretset OR p.prorettype != p2.prorettype OR
-     p2.pronargs != 1 OR
-     a.aggtranstype != p2.proargtypes[0]);
+    a.aggfinalfn = pfn.oid AND
+    (pfn.proretset
+     OR NOT binary_coercible(pfn.prorettype, p.prorettype)
+     OR pfn.pronargs != 1
+     OR NOT binary_coercible(a.aggtranstype, pfn.proargtypes[0]));
 
 -- If transfn is strict then either initval should be non-NULL, or
--- input type should equal transtype so that the first non-null input
+-- input type should match transtype so that the first non-null input
 -- can be assigned as the state value.
 
-SELECT a.aggfnoid::oid, p.proname, p2.oid, p2.proname
-FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS p2
+SELECT a.aggfnoid::oid, p.proname, ptr.oid, ptr.proname
+FROM pg_aggregate AS a, pg_proc AS p, pg_proc AS ptr
 WHERE a.aggfnoid = p.oid AND
-    a.aggtransfn = p2.oid AND p2.proisstrict AND
-    a.agginitval IS NULL AND p.proargtypes[0] != a.aggtranstype;
+    a.aggtransfn = ptr.oid AND ptr.proisstrict AND
+    a.agginitval IS NULL AND
+    NOT binary_coercible(p.proargtypes[0], a.aggtranstype);
 
 -- **************** pg_opclass ****************
 
@@ -592,7 +602,8 @@ WHERE p1.amopopr = p2.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname, p3.opcname
 FROM pg_amop AS p1, pg_operator AS p2, pg_opclass AS p3
 WHERE p1.amopopr = p2.oid AND p1.amopclaid = p3.oid AND
-    (p3.opcintype != p2.oprleft OR p3.opcintype != p2.oprright);
+    (NOT binary_coercible(p3.opcintype, p2.oprleft) OR
+     p2.oprleft != p2.oprright);
 
 -- **************** pg_amproc ****************
 
@@ -622,7 +633,8 @@ WHERE p2.opcamid = p1.oid AND
 -- signature of the function may be different for different support routines
 -- or different base data types.
 -- We can check that all the referenced instances of the same support
--- routine number take the same number of parameters, but that's about it...
+-- routine number take the same number of parameters, but that's about it
+-- for a general check...
 
 SELECT p1.amopclaid, p1.amprocnum,
 	p2.oid, p2.proname,
@@ -636,3 +648,20 @@ WHERE p1.amopclaid = p3.oid AND p4.amopclaid = p6.oid AND
     p3.opcamid = p6.opcamid AND p1.amprocnum = p4.amprocnum AND
     p1.amproc = p2.oid AND p4.amproc = p5.oid AND
     (p2.proretset OR p5.proretset OR p2.pronargs != p5.pronargs);
+
+-- For btree, though, we can do better since we know the support routines
+-- must be of the form cmp(input, input) returns int4.
+
+SELECT p1.amopclaid, p1.amprocnum,
+	p2.oid, p2.proname,
+	p3.opcname
+FROM pg_amproc AS p1, pg_proc AS p2, pg_opclass AS p3
+WHERE p3.opcamid = (SELECT oid FROM pg_am WHERE amname = 'btree')
+    AND p1.amopclaid = p3.oid AND p1.amproc = p2.oid AND
+    (opckeytype != 0
+     OR amprocnum != 1
+     OR proretset
+     OR prorettype != 23
+     OR pronargs != 2
+     OR NOT binary_coercible(opcintype, proargtypes[0])
+     OR proargtypes[0] != proargtypes[1]);
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index b84d0cb8adb00d36bb9c479a2798e41fde20d137..d2368d6ba534ab537f868996e5238ed285e684dd 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -199,7 +199,7 @@ SELECT 'unknown' || ' and unknown' AS "Concat unknown types";
 
 SELECT text 'text' || ' and unknown' AS "Concat text to unknown type";
 
-SELECT char(20) 'characters' || 'and text' AS "Concat char to unknown type";
+SELECT char(20) 'characters' || ' and text' AS "Concat char to unknown type";
 
 SELECT text 'text' || char(20) ' and characters' AS "Concat text to char";
 
diff --git a/src/test/regress/sql/union.sql b/src/test/regress/sql/union.sql
index 747d281948ff24e215eebd6d43552dd8130c425d..c69e4c3f15271334cb742b7a4c272f3fb160d435 100644
--- a/src/test/regress/sql/union.sql
+++ b/src/test/regress/sql/union.sql
@@ -66,13 +66,13 @@ UNION
 SELECT f1 FROM INT4_TBL
   WHERE f1 BETWEEN 0 AND 1000000;
 
-SELECT f1 AS five FROM VARCHAR_TBL
+SELECT CAST(f1 AS char(4)) AS three FROM VARCHAR_TBL
 UNION
 SELECT f1 FROM CHAR_TBL;
 
 SELECT f1 AS three FROM VARCHAR_TBL
 UNION
-SELECT TRIM(TRAILING FROM f1) FROM CHAR_TBL;
+SELECT CAST(f1 AS varchar) FROM CHAR_TBL;
 
 SELECT f1 AS eight FROM VARCHAR_TBL
 UNION ALL