Commit dc77be04 authored by Tom Lane's avatar Tom Lane

Fix executor to work correctly with mergejoins where left and

right sides have different data types.
parent 98f73945
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -67,6 +67,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_operator.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/execdefs.h" #include "executor/execdefs.h"
#include "executor/nodeMergejoin.h" #include "executor/nodeMergejoin.h"
...@@ -87,28 +88,31 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) ...@@ -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 * This takes the mergeclause which is a qualification of the
* form ((= expr expr) (= expr expr) ...) and forms a new * form ((= expr expr) (= expr expr) ...) and forms a new
* qualification like ((> expr expr) (> expr expr) ...) which * qualification like ((> expr expr) (> expr expr) ...) which
* is used by ExecMergeJoin() in order to determine if we should * is used by ExecMergeJoin() in order to determine if we should
* skip tuples. * skip tuples. The replacement operators are named either ">"
* * or "<" according to the replaceopname parameter, and have the
* old comments * same operand data types as the "=" operators they replace.
* The 'qual' must be of the form: * (We expect there to be such operators because the "=" operators
* {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...} * were marked mergejoinable; however, there might be a different
* The "sortOp outerkey innerkey" is formed by substituting the "=" * one needed in each qual clause.)
* by "sortOp".
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static List * static List *
MJFormOSortopI(List *qualList, Oid sortOp) MJFormSkipQual(List *qualList, char * replaceopname)
{ {
List *qualCopy; List *qualCopy;
List *qualcdr; List *qualcdr;
Expr *qual; Expr *qual;
Oper *op; Oper *op;
HeapTuple optup;
Form_pg_operator opform;
Oid oprleft,
oprright;
/* ---------------- /* ----------------
* qualList is a list: ((op .. ..) ...) * qualList is a list: ((op .. ..) ...)
...@@ -132,73 +136,50 @@ MJFormOSortopI(List *qualList, Oid sortOp) ...@@ -132,73 +136,50 @@ MJFormOSortopI(List *qualList, Oid sortOp)
*/ */
op = (Oper *) qual->oper; op = (Oper *) qual->oper;
if (!IsA(op, Oper)) if (!IsA(op, Oper))
{ elog(ERROR, "MJFormSkipQual: op not an Oper!");
elog(DEBUG, "MJFormOSortopI: op not an Oper!");
return NIL;
}
/* ---------------- /* ----------------
* change it's opid and since Op nodes now carry around a * Get the declared left and right operand types of the operator.
* cached pointer to the associated op function, we have * Note we do *not* use the actual operand types, since those might
* to make sure we invalidate this. Otherwise you get bizarre * be different in scenarios with binary-compatible data types.
* behavior when someone runs a mergejoin with _exec_repeat_ > 1 * There should be "<" and ">" operators matching a mergejoinable
* -cim 4/23/91 * "=" operator's declared operand types, but we might not find them
* if we search with the actual operand types.
* ---------------- * ----------------
*/ */
op->opid = sortOp; optup = get_operator_tuple(op->opno);
op->op_fcache = NULL; if (!HeapTupleIsValid(optup)) /* shouldn't happen */
} elog(ERROR, "MJFormSkipQual: operator %d not found", op->opno);
opform = (Form_pg_operator) GETSTRUCT(optup);
return qualCopy; oprleft = opform->oprleft;
} oprright = opform->oprright;
/* ----------------------------------------------------------------
* 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;
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)); * And replace the data in the copied operator node.
lfirst(qual->args) = outer; * ----------------
lfirst(lnext(qual->args)) = inner; */
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) ...@@ -215,6 +196,7 @@ MJFormISortopO(List *qualList, Oid sortOp)
* the first keys being most significant. Therefore, the clauses * the first keys being most significant. Therefore, the clauses
* are evaluated in order and the 'compareQual' is satisfied * are evaluated in order and the 'compareQual' is satisfied
* if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i. * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
* We use the original mergeclause items to detect equality.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static bool static bool
...@@ -386,12 +368,16 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate) ...@@ -386,12 +368,16 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
* *
* Therefore, when initializing the merge-join node, the executor * Therefore, when initializing the merge-join node, the executor
* creates the "greater/smaller" clause by substituting the "=" * creates the "greater/smaller" clause by substituting the "="
* operator in the join clauses with the sort operator used to * operator in the join clauses with the corresponding ">" operator.
* sort the outer and inner relation forming (outerKey sortOp innerKey). * The opposite "smaller/greater" clause is formed by substituting "<".
* The sort operator is "<" if the relations are in ascending order *
* otherwise, it is ">" if the relations are in descending order. * Note: prior to v6.5, the relational clauses were formed using the
* The opposite "smaller/greater" clause is formed by reversing the * sort op used to sort the inner relation, which of course would fail
* outer and inner keys forming (innerKey sortOp outerKey). * 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" * (2) repositioning inner "cursor"
* *
...@@ -452,13 +438,13 @@ ExecMergeJoin(MergeJoin *node) ...@@ -452,13 +438,13 @@ ExecMergeJoin(MergeJoin *node)
if (ScanDirectionIsForward(direction)) if (ScanDirectionIsForward(direction))
{ {
outerSkipQual = mergestate->mj_OSortopI; outerSkipQual = mergestate->mj_OuterSkipQual;
innerSkipQual = mergestate->mj_ISortopO; innerSkipQual = mergestate->mj_InnerSkipQual;
} }
else else
{ {
outerSkipQual = mergestate->mj_ISortopO; outerSkipQual = mergestate->mj_InnerSkipQual;
innerSkipQual = mergestate->mj_OSortopI; innerSkipQual = mergestate->mj_OuterSkipQual;
} }
/* ---------------- /* ----------------
...@@ -1130,14 +1116,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) ...@@ -1130,14 +1116,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
{ {
MergeJoinState *mergestate; MergeJoinState *mergestate;
List *joinclauses; List *joinclauses;
RegProcedure rightsortop;
RegProcedure leftsortop;
RegProcedure sortop;
TupleTableSlot *mjSlot; TupleTableSlot *mjSlot;
List *OSortopI;
List *ISortopO;
MJ1_printf("ExecInitMergeJoin: %s\n", MJ1_printf("ExecInitMergeJoin: %s\n",
"initializing node"); "initializing node");
...@@ -1153,14 +1133,14 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) ...@@ -1153,14 +1133,14 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
* ---------------- * ----------------
*/ */
mergestate = makeNode(MergeJoinState); mergestate = makeNode(MergeJoinState);
mergestate->mj_OSortopI = NIL; mergestate->mj_OuterSkipQual = NIL;
mergestate->mj_ISortopO = NIL; mergestate->mj_InnerSkipQual = NIL;
mergestate->mj_JoinState = 0; mergestate->mj_JoinState = 0;
mergestate->mj_MarkedTupleSlot = NULL; mergestate->mj_MarkedTupleSlot = NULL;
node->mergestate = mergestate; node->mergestate = mergestate;
/* ---------------- /* ----------------
* Miscellanious initialization * Miscellaneous initialization
* *
* + assign node's base_id * + assign node's base_id
* + assign debugging hooks and * + assign debugging hooks and
...@@ -1185,40 +1165,17 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) ...@@ -1185,40 +1165,17 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
mergestate->mj_MarkedTupleSlot = mjSlot; mergestate->mj_MarkedTupleSlot = mjSlot;
/* ---------------- /* ----------------
* get merge sort operators. * form merge skip qualifications
*
* 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
* ---------------- * ----------------
*/ */
joinclauses = node->mergeclauses; joinclauses = node->mergeclauses;
mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
rightsortop = get_opcode(node->mergerightorder[0]); MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
leftsortop = get_opcode(node->mergeleftorder[0]); MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
if (leftsortop != rightsortop) MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
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("\n"); MJ_printf("\n");
/* ---------------- /* ----------------
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * 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 ...@@ -463,8 +463,8 @@ typedef struct NestLoopState
/* ---------------- /* ----------------
* MergeJoinState information * MergeJoinState information
* *
* OSortopI outerKey1 sortOp innerKey1 ... * OuterSkipQual outerKey1 < innerKey1 ...
* ISortopO innerkey1 sortOp outerkey1 ... * InnerSkipQual outerKey1 > innerKey1 ...
* JoinState current "state" of join. see executor.h * JoinState current "state" of join. see executor.h
* MarkedTupleSlot pointer to slot in tuple table for marked tuple * MarkedTupleSlot pointer to slot in tuple table for marked tuple
* *
...@@ -483,8 +483,8 @@ typedef struct NestLoopState ...@@ -483,8 +483,8 @@ typedef struct NestLoopState
typedef struct MergeJoinState typedef struct MergeJoinState
{ {
JoinState jstate; /* its first field is NodeTag */ JoinState jstate; /* its first field is NodeTag */
List *mj_OSortopI; List *mj_OuterSkipQual;
List *mj_ISortopO; List *mj_InnerSkipQual;
int mj_JoinState; int mj_JoinState;
TupleTableSlot *mj_MarkedTupleSlot; TupleTableSlot *mj_MarkedTupleSlot;
} MergeJoinState; } MergeJoinState;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment