diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 9fc0aed1126d692b7f4d29833dec6a1b761f980f..4ecde52ab33a4390c9da6918c328d731c1118a47 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.24 1999/02/24 10:20:07 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.25 1999/02/28 00:36:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,6 +67,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "catalog/pg_operator.h"
 #include "executor/executor.h"
 #include "executor/execdefs.h"
 #include "executor/nodeMergejoin.h"
@@ -87,28 +88,31 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 
 
 /* ----------------------------------------------------------------
- *		MJFormOSortopI
+ *		MJFormSkipQual
  *
  *		This takes the mergeclause which is a qualification of the
  *		form ((= expr expr) (= expr expr) ...) and forms a new
  *		qualification like ((> expr expr) (> expr expr) ...) which
  *		is used by ExecMergeJoin() in order to determine if we should
- *		skip tuples.
- *
- * old comments
- *		The 'qual' must be of the form:
- *		   {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...}
- *		The "sortOp outerkey innerkey" is formed by substituting the "="
- *		by "sortOp".
+ *		skip tuples.  The replacement operators are named either ">"
+ *		or "<" according to the replaceopname parameter, and have the
+ *		same operand data types as the "=" operators they replace.
+ *		(We expect there to be such operators because the "=" operators
+ *		were marked mergejoinable; however, there might be a different
+ *		one needed in each qual clause.)
  * ----------------------------------------------------------------
  */
 static List *
-MJFormOSortopI(List *qualList, Oid sortOp)
+MJFormSkipQual(List *qualList, char * replaceopname)
 {
 	List	   *qualCopy;
 	List	   *qualcdr;
 	Expr	   *qual;
 	Oper	   *op;
+	HeapTuple	optup;
+	Form_pg_operator opform;
+	Oid			oprleft,
+				oprright;
 
 	/* ----------------
 	 *	qualList is a list: ((op .. ..) ...)
@@ -132,73 +136,50 @@ MJFormOSortopI(List *qualList, Oid sortOp)
 		 */
 		op = (Oper *) qual->oper;
 		if (!IsA(op, Oper))
-		{
-			elog(DEBUG, "MJFormOSortopI: op not an Oper!");
-			return NIL;
-		}
+			elog(ERROR, "MJFormSkipQual: op not an Oper!");
 
 		/* ----------------
-		 *	 change it's opid and since Op nodes now carry around a
-		 *	 cached pointer to the associated op function, we have
-		 *	 to make sure we invalidate this.  Otherwise you get bizarre
-		 *	 behavior when someone runs a mergejoin with _exec_repeat_ > 1
-		 *	 -cim 4/23/91
+		 *	 Get the declared left and right operand types of the operator.
+		 *	 Note we do *not* use the actual operand types, since those might
+		 *	 be different in scenarios with binary-compatible data types.
+		 *	 There should be "<" and ">" operators matching a mergejoinable
+		 *	 "=" operator's declared operand types, but we might not find them
+		 *	 if we search with the actual operand types.
 		 * ----------------
 		 */
-		op->opid = sortOp;
-		op->op_fcache = NULL;
-	}
-
-	return qualCopy;
-}
-
-/* ----------------------------------------------------------------
- *		MJFormISortopO
- *
- *		This does the same thing as MJFormOSortopI() except that
- *		it also reverses the expressions in the qualifications.
- *		For example: ((= expr1 expr2)) produces ((> expr2 expr1))
- *
- * old comments
- *		The 'qual' must be of the form:
- *		   {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...}
- *		The 'sortOp innerkey1 outerkey" is formed by substituting the "="
- *		by "sortOp" and reversing the positions of the keys.
- *	----------------------------------------------------------------
- */
-static List *
-MJFormISortopO(List *qualList, Oid sortOp)
-{
-	List	   *ISortopO;
-	List	   *qualcdr;
-
-	/* ----------------
-	 *	first generate OSortopI, a list of the form
-	 *	((op outer inner) (op outer inner) ... )
-	 * ----------------
-	 */
-	ISortopO = MJFormOSortopI(qualList, sortOp);
-
-	/* ----------------
-	 *	now swap the cadr and caddr of each qual to form ISortopO,
-	 *	((op inner outer) (op inner outer) ... )
-	 * ----------------
-	 */
-	foreach(qualcdr, ISortopO)
-	{
-		Expr	   *qual;
-		List	   *inner;
-		List	   *outer;
+		optup = get_operator_tuple(op->opno);
+		if (!HeapTupleIsValid(optup))		/* shouldn't happen */
+			elog(ERROR, "MJFormSkipQual: operator %d not found", op->opno);
+		opform = (Form_pg_operator) GETSTRUCT(optup);
+		oprleft = opform->oprleft;
+		oprright = opform->oprright;
 
-		qual = lfirst(qualcdr);
+		/* ----------------
+		 *	 Now look up the matching "<" or ">" operator.  If there isn't one,
+		 *	 whoever marked the "=" operator mergejoinable was a loser.
+		 * ----------------
+		 */
+		optup = SearchSysCacheTuple(OPRNAME,
+									PointerGetDatum(replaceopname),
+									ObjectIdGetDatum(oprleft),
+									ObjectIdGetDatum(oprright),
+									CharGetDatum('b'));
+		if (!HeapTupleIsValid(optup))
+			elog(ERROR,
+				 "MJFormSkipQual: mergejoin operator %d has no matching %s op",
+				 op->opno, replaceopname);
+		opform = (Form_pg_operator) GETSTRUCT(optup);
 
-		inner = lfirst(qual->args);
-		outer = lfirst(lnext(qual->args));
-		lfirst(qual->args) = outer;
-		lfirst(lnext(qual->args)) = inner;
+		/* ----------------
+		 *	 And replace the data in the copied operator node.
+		 * ----------------
+		 */
+		op->opno = optup->t_data->t_oid;
+		op->opid = opform->oprcode;
+		op->op_fcache = NULL;
 	}
 
-	return ISortopO;
+	return qualCopy;
 }
 
 /* ----------------------------------------------------------------
@@ -215,6 +196,7 @@ MJFormISortopO(List *qualList, Oid sortOp)
  *		the first keys being most significant. Therefore, the clauses
  *		are evaluated in order and the 'compareQual' is satisfied
  *		if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
+ *		We use the original mergeclause items to detect equality.
  * ----------------------------------------------------------------
  */
 static bool
@@ -386,12 +368,16 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
  *
  *		Therefore, when initializing the merge-join node, the executor
  *		creates the "greater/smaller" clause by substituting the "="
- *		operator in the join clauses with the sort operator used to
- *		sort the outer and inner relation forming (outerKey sortOp innerKey).
- *		The sort operator is "<" if the relations are in ascending order
- *		otherwise, it is ">" if the relations are in descending order.
- *		The opposite "smaller/greater" clause is formed by reversing the
- *		outer and inner keys forming (innerKey sortOp outerKey).
+ *		operator in the join clauses with the corresponding ">" operator.
+ *		The opposite "smaller/greater" clause is formed by substituting "<".
+ *
+ *		Note: prior to v6.5, the relational clauses were formed using the
+ *		sort op used to sort the inner relation, which of course would fail
+ *		if the outer and inner keys were of different data types.
+ *		In the current code, we instead assume that operators named "<" and ">"
+ *		will do the right thing.  This should be true since the mergejoin "="
+ *		operator's pg_operator entry will have told the planner to sort by
+ *		"<" for each of the left and right sides.
  *
  *		(2) repositioning inner "cursor"
  *
@@ -452,13 +438,13 @@ ExecMergeJoin(MergeJoin *node)
 
 	if (ScanDirectionIsForward(direction))
 	{
-		outerSkipQual = mergestate->mj_OSortopI;
-		innerSkipQual = mergestate->mj_ISortopO;
+		outerSkipQual = mergestate->mj_OuterSkipQual;
+		innerSkipQual = mergestate->mj_InnerSkipQual;
 	}
 	else
 	{
-		outerSkipQual = mergestate->mj_ISortopO;
-		innerSkipQual = mergestate->mj_OSortopI;
+		outerSkipQual = mergestate->mj_InnerSkipQual;
+		innerSkipQual = mergestate->mj_OuterSkipQual;
 	}
 
 	/* ----------------
@@ -1130,14 +1116,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 {
 	MergeJoinState *mergestate;
 	List	   *joinclauses;
-	RegProcedure rightsortop;
-	RegProcedure leftsortop;
-	RegProcedure sortop;
 	TupleTableSlot *mjSlot;
 
-	List	   *OSortopI;
-	List	   *ISortopO;
-
 	MJ1_printf("ExecInitMergeJoin: %s\n",
 			   "initializing node");
 
@@ -1153,14 +1133,14 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	 * ----------------
 	 */
 	mergestate = makeNode(MergeJoinState);
-	mergestate->mj_OSortopI = NIL;
-	mergestate->mj_ISortopO = NIL;
+	mergestate->mj_OuterSkipQual = NIL;
+	mergestate->mj_InnerSkipQual = NIL;
 	mergestate->mj_JoinState = 0;
 	mergestate->mj_MarkedTupleSlot = NULL;
 	node->mergestate = mergestate;
 
 	/* ----------------
-	 *	Miscellanious initialization
+	 *	Miscellaneous initialization
 	 *
 	 *		 +	assign node's base_id
 	 *		 +	assign debugging hooks and
@@ -1185,40 +1165,17 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	mergestate->mj_MarkedTupleSlot = mjSlot;
 
 	/* ----------------
-	 *	get merge sort operators.
-	 *
-	 *	XXX for now we assume all quals in the joinclauses were
-	 *		sorted with the same operator in both the inner and
-	 *		outer relations. -cim 11/2/89
+	 *	form merge skip qualifications
 	 * ----------------
 	 */
 	joinclauses = node->mergeclauses;
+	mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
+	mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
 
-	rightsortop = get_opcode(node->mergerightorder[0]);
-	leftsortop = get_opcode(node->mergeleftorder[0]);
-
-	if (leftsortop != rightsortop)
-		elog(NOTICE, "ExecInitMergeJoin: %s",
-			 "left and right sortop's are unequal!");
-
-	sortop = rightsortop;
-
-	/* ----------------
-	 *	form merge skip qualifications
-	 *
-	 *	XXX MJform routines need to be extended
-	 *		to take a list of sortops.. -cim 11/2/89
-	 * ----------------
-	 */
-	OSortopI = MJFormOSortopI(joinclauses, sortop);
-	ISortopO = MJFormISortopO(joinclauses, sortop);
-	mergestate->mj_OSortopI = OSortopI;
-	mergestate->mj_ISortopO = ISortopO;
-
-	MJ_printf("\nExecInitMergeJoin: OSortopI is ");
-	MJ_nodeDisplay(OSortopI);
-	MJ_printf("\nExecInitMergeJoin: ISortopO is ");
-	MJ_nodeDisplay(ISortopO);
+	MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
+	MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
+	MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
+	MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
 	MJ_printf("\n");
 
 	/* ----------------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 59962ddb8087cc42c6187565db7261871001011c..c7f6fa35b2d245a09ead74539f4e82395f52590f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.25 1999/02/23 07:55:23 thomas Exp $
+ * $Id: execnodes.h,v 1.26 1999/02/28 00:36:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -463,8 +463,8 @@ typedef struct NestLoopState
 /* ----------------
  *	 MergeJoinState information
  *
- *		OSortopI		   outerKey1 sortOp innerKey1 ...
- *		ISortopO		   innerkey1 sortOp outerkey1 ...
+ *		OuterSkipQual	   outerKey1 < innerKey1 ...
+ *		InnerSkipQual	   outerKey1 > innerKey1 ...
  *		JoinState		   current "state" of join. see executor.h
  *		MarkedTupleSlot    pointer to slot in tuple table for marked tuple
  *
@@ -483,8 +483,8 @@ typedef struct NestLoopState
 typedef struct MergeJoinState
 {
 	JoinState	jstate;			/* its first field is NodeTag */
-	List	   *mj_OSortopI;
-	List	   *mj_ISortopO;
+	List	   *mj_OuterSkipQual;
+	List	   *mj_InnerSkipQual;
 	int			mj_JoinState;
 	TupleTableSlot *mj_MarkedTupleSlot;
 } MergeJoinState;