]> granicus.if.org Git - postgresql/commitdiff
Fix executor to work correctly with mergejoins where left and
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Feb 1999 00:36:05 +0000 (00:36 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 28 Feb 1999 00:36:05 +0000 (00:36 +0000)
right sides have different data types.

src/backend/executor/nodeMergejoin.c
src/include/nodes/execnodes.h

index 9fc0aed1126d692b7f4d29833dec6a1b761f980f..4ecde52ab33a4390c9da6918c328d731c1118a47 100644 (file)
@@ -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");
 
        /* ----------------
index 59962ddb8087cc42c6187565db7261871001011c..c7f6fa35b2d245a09ead74539f4e82395f52590f 100644 (file)
@@ -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;