Skip to content

Commit dc77be0

Browse files
committed
Fix executor to work correctly with mergejoins where left and
right sides have different data types.
1 parent 98f7394 commit dc77be0

File tree

2 files changed

+80
-123
lines changed

2 files changed

+80
-123
lines changed

src/backend/executor/nodeMergejoin.c

Lines changed: 75 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.24 1999/02/24 10:20:07 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.25 1999/02/28 00:36:05 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -67,6 +67,7 @@
6767
#include "postgres.h"
6868

6969
#include "access/heapam.h"
70+
#include "catalog/pg_operator.h"
7071
#include "executor/executor.h"
7172
#include "executor/execdefs.h"
7273
#include "executor/nodeMergejoin.h"
@@ -87,28 +88,31 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
8788

8889

8990
/* ----------------------------------------------------------------
90-
* MJFormOSortopI
91+
* MJFormSkipQual
9192
*
9293
* This takes the mergeclause which is a qualification of the
9394
* form ((= expr expr) (= expr expr) ...) and forms a new
9495
* qualification like ((> expr expr) (> expr expr) ...) which
9596
* is used by ExecMergeJoin() in order to determine if we should
96-
* skip tuples.
97-
*
98-
* old comments
99-
* The 'qual' must be of the form:
100-
* {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...}
101-
* The "sortOp outerkey innerkey" is formed by substituting the "="
102-
* by "sortOp".
97+
* skip tuples. The replacement operators are named either ">"
98+
* or "<" according to the replaceopname parameter, and have the
99+
* same operand data types as the "=" operators they replace.
100+
* (We expect there to be such operators because the "=" operators
101+
* were marked mergejoinable; however, there might be a different
102+
* one needed in each qual clause.)
103103
* ----------------------------------------------------------------
104104
*/
105105
static List *
106-
MJFormOSortopI(List *qualList, Oid sortOp)
106+
MJFormSkipQual(List *qualList, char * replaceopname)
107107
{
108108
List *qualCopy;
109109
List *qualcdr;
110110
Expr *qual;
111111
Oper *op;
112+
HeapTuple optup;
113+
Form_pg_operator opform;
114+
Oid oprleft,
115+
oprright;
112116

113117
/* ----------------
114118
* qualList is a list: ((op .. ..) ...)
@@ -132,73 +136,50 @@ MJFormOSortopI(List *qualList, Oid sortOp)
132136
*/
133137
op = (Oper *) qual->oper;
134138
if (!IsA(op, Oper))
135-
{
136-
elog(DEBUG, "MJFormOSortopI: op not an Oper!");
137-
return NIL;
138-
}
139+
elog(ERROR, "MJFormSkipQual: op not an Oper!");
139140

140141
/* ----------------
141-
* change it's opid and since Op nodes now carry around a
142-
* cached pointer to the associated op function, we have
143-
* to make sure we invalidate this. Otherwise you get bizarre
144-
* behavior when someone runs a mergejoin with _exec_repeat_ > 1
145-
* -cim 4/23/91
142+
* Get the declared left and right operand types of the operator.
143+
* Note we do *not* use the actual operand types, since those might
144+
* be different in scenarios with binary-compatible data types.
145+
* There should be "<" and ">" operators matching a mergejoinable
146+
* "=" operator's declared operand types, but we might not find them
147+
* if we search with the actual operand types.
146148
* ----------------
147149
*/
148-
op->opid = sortOp;
149-
op->op_fcache = NULL;
150-
}
151-
152-
return qualCopy;
153-
}
154-
155-
/* ----------------------------------------------------------------
156-
* MJFormISortopO
157-
*
158-
* This does the same thing as MJFormOSortopI() except that
159-
* it also reverses the expressions in the qualifications.
160-
* For example: ((= expr1 expr2)) produces ((> expr2 expr1))
161-
*
162-
* old comments
163-
* The 'qual' must be of the form:
164-
* {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...}
165-
* The 'sortOp innerkey1 outerkey" is formed by substituting the "="
166-
* by "sortOp" and reversing the positions of the keys.
167-
* ----------------------------------------------------------------
168-
*/
169-
static List *
170-
MJFormISortopO(List *qualList, Oid sortOp)
171-
{
172-
List *ISortopO;
173-
List *qualcdr;
174-
175-
/* ----------------
176-
* first generate OSortopI, a list of the form
177-
* ((op outer inner) (op outer inner) ... )
178-
* ----------------
179-
*/
180-
ISortopO = MJFormOSortopI(qualList, sortOp);
181-
182-
/* ----------------
183-
* now swap the cadr and caddr of each qual to form ISortopO,
184-
* ((op inner outer) (op inner outer) ... )
185-
* ----------------
186-
*/
187-
foreach(qualcdr, ISortopO)
188-
{
189-
Expr *qual;
190-
List *inner;
191-
List *outer;
150+
optup = get_operator_tuple(op->opno);
151+
if (!HeapTupleIsValid(optup)) /* shouldn't happen */
152+
elog(ERROR, "MJFormSkipQual: operator %d not found", op->opno);
153+
opform = (Form_pg_operator) GETSTRUCT(optup);
154+
oprleft = opform->oprleft;
155+
oprright = opform->oprright;
192156

193-
qual = lfirst(qualcdr);
157+
/* ----------------
158+
* Now look up the matching "<" or ">" operator. If there isn't one,
159+
* whoever marked the "=" operator mergejoinable was a loser.
160+
* ----------------
161+
*/
162+
optup = SearchSysCacheTuple(OPRNAME,
163+
PointerGetDatum(replaceopname),
164+
ObjectIdGetDatum(oprleft),
165+
ObjectIdGetDatum(oprright),
166+
CharGetDatum('b'));
167+
if (!HeapTupleIsValid(optup))
168+
elog(ERROR,
169+
"MJFormSkipQual: mergejoin operator %d has no matching %s op",
170+
op->opno, replaceopname);
171+
opform = (Form_pg_operator) GETSTRUCT(optup);
194172

195-
inner = lfirst(qual->args);
196-
outer = lfirst(lnext(qual->args));
197-
lfirst(qual->args) = outer;
198-
lfirst(lnext(qual->args)) = inner;
173+
/* ----------------
174+
* And replace the data in the copied operator node.
175+
* ----------------
176+
*/
177+
op->opno = optup->t_data->t_oid;
178+
op->opid = opform->oprcode;
179+
op->op_fcache = NULL;
199180
}
200181

201-
return ISortopO;
182+
return qualCopy;
202183
}
203184

204185
/* ----------------------------------------------------------------
@@ -215,6 +196,7 @@ MJFormISortopO(List *qualList, Oid sortOp)
215196
* the first keys being most significant. Therefore, the clauses
216197
* are evaluated in order and the 'compareQual' is satisfied
217198
* if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
199+
* We use the original mergeclause items to detect equality.
218200
* ----------------------------------------------------------------
219201
*/
220202
static bool
@@ -386,12 +368,16 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
386368
*
387369
* Therefore, when initializing the merge-join node, the executor
388370
* creates the "greater/smaller" clause by substituting the "="
389-
* operator in the join clauses with the sort operator used to
390-
* sort the outer and inner relation forming (outerKey sortOp innerKey).
391-
* The sort operator is "<" if the relations are in ascending order
392-
* otherwise, it is ">" if the relations are in descending order.
393-
* The opposite "smaller/greater" clause is formed by reversing the
394-
* outer and inner keys forming (innerKey sortOp outerKey).
371+
* operator in the join clauses with the corresponding ">" operator.
372+
* The opposite "smaller/greater" clause is formed by substituting "<".
373+
*
374+
* Note: prior to v6.5, the relational clauses were formed using the
375+
* sort op used to sort the inner relation, which of course would fail
376+
* if the outer and inner keys were of different data types.
377+
* In the current code, we instead assume that operators named "<" and ">"
378+
* will do the right thing. This should be true since the mergejoin "="
379+
* operator's pg_operator entry will have told the planner to sort by
380+
* "<" for each of the left and right sides.
395381
*
396382
* (2) repositioning inner "cursor"
397383
*
@@ -452,13 +438,13 @@ ExecMergeJoin(MergeJoin *node)
452438

453439
if (ScanDirectionIsForward(direction))
454440
{
455-
outerSkipQual = mergestate->mj_OSortopI;
456-
innerSkipQual = mergestate->mj_ISortopO;
441+
outerSkipQual = mergestate->mj_OuterSkipQual;
442+
innerSkipQual = mergestate->mj_InnerSkipQual;
457443
}
458444
else
459445
{
460-
outerSkipQual = mergestate->mj_ISortopO;
461-
innerSkipQual = mergestate->mj_OSortopI;
446+
outerSkipQual = mergestate->mj_InnerSkipQual;
447+
innerSkipQual = mergestate->mj_OuterSkipQual;
462448
}
463449

464450
/* ----------------
@@ -1130,14 +1116,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
11301116
{
11311117
MergeJoinState *mergestate;
11321118
List *joinclauses;
1133-
RegProcedure rightsortop;
1134-
RegProcedure leftsortop;
1135-
RegProcedure sortop;
11361119
TupleTableSlot *mjSlot;
11371120

1138-
List *OSortopI;
1139-
List *ISortopO;
1140-
11411121
MJ1_printf("ExecInitMergeJoin: %s\n",
11421122
"initializing node");
11431123

@@ -1153,14 +1133,14 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
11531133
* ----------------
11541134
*/
11551135
mergestate = makeNode(MergeJoinState);
1156-
mergestate->mj_OSortopI = NIL;
1157-
mergestate->mj_ISortopO = NIL;
1136+
mergestate->mj_OuterSkipQual = NIL;
1137+
mergestate->mj_InnerSkipQual = NIL;
11581138
mergestate->mj_JoinState = 0;
11591139
mergestate->mj_MarkedTupleSlot = NULL;
11601140
node->mergestate = mergestate;
11611141

11621142
/* ----------------
1163-
* Miscellanious initialization
1143+
* Miscellaneous initialization
11641144
*
11651145
* + assign node's base_id
11661146
* + assign debugging hooks and
@@ -1185,40 +1165,17 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
11851165
mergestate->mj_MarkedTupleSlot = mjSlot;
11861166

11871167
/* ----------------
1188-
* get merge sort operators.
1189-
*
1190-
* XXX for now we assume all quals in the joinclauses were
1191-
* sorted with the same operator in both the inner and
1192-
* outer relations. -cim 11/2/89
1168+
* form merge skip qualifications
11931169
* ----------------
11941170
*/
11951171
joinclauses = node->mergeclauses;
1172+
mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
1173+
mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
11961174

1197-
rightsortop = get_opcode(node->mergerightorder[0]);
1198-
leftsortop = get_opcode(node->mergeleftorder[0]);
1199-
1200-
if (leftsortop != rightsortop)
1201-
elog(NOTICE, "ExecInitMergeJoin: %s",
1202-
"left and right sortop's are unequal!");
1203-
1204-
sortop = rightsortop;
1205-
1206-
/* ----------------
1207-
* form merge skip qualifications
1208-
*
1209-
* XXX MJform routines need to be extended
1210-
* to take a list of sortops.. -cim 11/2/89
1211-
* ----------------
1212-
*/
1213-
OSortopI = MJFormOSortopI(joinclauses, sortop);
1214-
ISortopO = MJFormISortopO(joinclauses, sortop);
1215-
mergestate->mj_OSortopI = OSortopI;
1216-
mergestate->mj_ISortopO = ISortopO;
1217-
1218-
MJ_printf("\nExecInitMergeJoin: OSortopI is ");
1219-
MJ_nodeDisplay(OSortopI);
1220-
MJ_printf("\nExecInitMergeJoin: ISortopO is ");
1221-
MJ_nodeDisplay(ISortopO);
1175+
MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1176+
MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1177+
MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1178+
MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
12221179
MJ_printf("\n");
12231180

12241181
/* ----------------

src/include/nodes/execnodes.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
* Copyright (c) 1994, Regents of the University of California
88
*
9-
* $Id: execnodes.h,v 1.25 1999/02/23 07:55:23 thomas Exp $
9+
* $Id: execnodes.h,v 1.26 1999/02/28 00:36:04 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -463,8 +463,8 @@ typedef struct NestLoopState
463463
/* ----------------
464464
* MergeJoinState information
465465
*
466-
* OSortopI outerKey1 sortOp innerKey1 ...
467-
* ISortopO innerkey1 sortOp outerkey1 ...
466+
* OuterSkipQual outerKey1 < innerKey1 ...
467+
* InnerSkipQual outerKey1 > innerKey1 ...
468468
* JoinState current "state" of join. see executor.h
469469
* MarkedTupleSlot pointer to slot in tuple table for marked tuple
470470
*
@@ -483,8 +483,8 @@ typedef struct NestLoopState
483483
typedef struct MergeJoinState
484484
{
485485
JoinState jstate; /* its first field is NodeTag */
486-
List *mj_OSortopI;
487-
List *mj_ISortopO;
486+
List *mj_OuterSkipQual;
487+
List *mj_InnerSkipQual;
488488
int mj_JoinState;
489489
TupleTableSlot *mj_MarkedTupleSlot;
490490
} MergeJoinState;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy