88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.85 2007/01/10 18:06:02 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.86 2007/01/11 17:19:13 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
3939 *therefore it should scan the outer relation first to find a
4040 *matching tuple and so on.
4141 *
42- *Therefore, when initializing the merge-join node, we look up the
43- *associated sort operators.We assume the planner has seen to it
44- *that the inputs are correctly sorted by these operators. Rather
45- *than directly executing the merge join clauses, we evaluate the
46- *left and right key expressions separately and then compare the
47- *columns one at a time (see MJCompare).
42+ *Therefore, rather than directly executing the merge join clauses,
43+ *we evaluate the left and right key expressions separately and then
44+ *compare the columns one at a time (see MJCompare). The planner
45+ *passes us enough information about the sort ordering of the inputs
46+ *to allow us to determine how to make the comparison. We may use the
47+ *appropriate btree comparison function, since Postgres' only notion
48+ *of ordering is specified by btree opfamilies.
4849 *
4950 *
5051 *Consider the above relations and suppose that the executor has
104105
105106
106107/*
107- * Comparison strategies supported by MJCompare
108- *
109- * XXX eventually should extend MJCompare to support descending-order sorts.
110- * There are some tricky issues however about being sure we are on the same
111- * page as the underlying sort or index as to which end NULLs sort to.
108+ * Runtime data for each mergejoin clause
112109 */
113- typedef enum
114- {
115- MERGEFUNC_CMP ,/* -1 / 0 / 1 three-way comparator */
116- MERGEFUNC_REV_CMP /* same, reversing the sense of the result */
117- }MergeFunctionKind ;
118-
119- /* Runtime data for each mergejoin clause */
120110typedef struct MergeJoinClauseData
121111{
122112/* Executable expression trees */
@@ -136,7 +126,8 @@ typedef struct MergeJoinClauseData
136126 * The comparison strategy in use, and the lookup info to let us call the
137127 * btree comparison support function.
138128 */
139- MergeFunctionKind cmpstrategy ;
129+ bool reverse ;/* if true, negate the cmpfn's output */
130+ bool nulls_first ;/* if true, nulls sort low */
140131FmgrInfo cmpfinfo ;
141132}MergeJoinClauseData ;
142133
@@ -158,11 +149,11 @@ typedef struct MergeJoinClauseData
158149 * In addition to the expressions themselves, the planner passes the btree
159150 * opfamily OID, btree strategy number (BTLessStrategyNumber or
160151 * BTGreaterStrategyNumber), and nulls-first flag that identify the intended
161- *merge semantics for each merge key. The mergejoinable operator is an
152+ *sort ordering for each merge key. The mergejoinable operator is an
162153 * equality operator in this opfamily, and the two inputs are guaranteed to be
163154 * ordered in either increasing or decreasing (respectively) order according
164- * to this opfamily. This allows us to obtain theneeded comparison functions
165- * from the opfamily.
155+ * to this opfamily, with nulls at the indicated end of therange. This
156+ *allows us to obtain the needed comparison function from the opfamily.
166157 */
167158static MergeJoinClause
168159MJExamineQuals (List * mergeclauses ,
@@ -193,11 +184,6 @@ MJExamineQuals(List *mergeclauses,
193184RegProcedure cmpproc ;
194185AclResult aclresult ;
195186
196- /* Later we'll support both ascending and descending sort... */
197- Assert (opstrategy == BTLessStrategyNumber );
198- clause -> cmpstrategy = MERGEFUNC_CMP ;
199- Assert (!nulls_first );
200-
201187if (!IsA (qual ,OpExpr ))
202188elog (ERROR ,"mergejoin clause is not an OpExpr" );
203189
@@ -213,15 +199,19 @@ MJExamineQuals(List *mergeclauses,
213199& op_lefttype ,
214200& op_righttype ,
215201& op_recheck );
216- Assert (op_strategy == BTEqualStrategyNumber );
217- Assert (!op_recheck );
202+ if (op_strategy != BTEqualStrategyNumber )/* should not happen */
203+ elog (ERROR ,"cannot merge using non-equality operator %u" ,
204+ qual -> opno );
205+ Assert (!op_recheck );/* never true for btree */
218206
219207/* And get the matching support procedure (comparison function) */
220208cmpproc = get_opfamily_proc (opfamily ,
221209op_lefttype ,
222210op_righttype ,
223211BTORDER_PROC );
224- Assert (RegProcedureIsValid (cmpproc ));
212+ if (!RegProcedureIsValid (cmpproc ))/* should not happen */
213+ elog (ERROR ,"missing support function %d(%u,%u) in opfamily %u" ,
214+ BTORDER_PROC ,op_lefttype ,op_righttype ,opfamily );
225215
226216/* Check permission to call cmp function */
227217aclresult = pg_proc_aclcheck (cmpproc ,GetUserId (),ACL_EXECUTE );
@@ -232,6 +222,16 @@ MJExamineQuals(List *mergeclauses,
232222/* Set up the fmgr lookup information */
233223fmgr_info (cmpproc ,& (clause -> cmpfinfo ));
234224
225+ /* Fill the additional comparison-strategy flags */
226+ if (opstrategy == BTLessStrategyNumber )
227+ clause -> reverse = false;
228+ else if (opstrategy == BTGreaterStrategyNumber )
229+ clause -> reverse = true;
230+ else /* planner screwed up */
231+ elog (ERROR ,"unsupported mergejoin strategy %d" ,opstrategy );
232+
233+ clause -> nulls_first = nulls_first ;
234+
235235iClause ++ ;
236236}
237237
@@ -324,10 +324,10 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
324324 * MJEvalOuterValues and MJEvalInnerValues must already have been called
325325 * for the current outer and inner tuples, respectively.
326326 */
327- static int
327+ static int32
328328MJCompare (MergeJoinState * mergestate )
329329{
330- int result = 0 ;
330+ int32 result = 0 ;
331331bool nulleqnull = false;
332332ExprContext * econtext = mergestate -> js .ps .ps_ExprContext ;
333333int i ;
@@ -348,26 +348,33 @@ MJCompare(MergeJoinState *mergestate)
348348Datum fresult ;
349349
350350/*
351- * Deal with null inputs. We treat NULL as sorting after non-NULL.
351+ * Deal with null inputs.
352352 */
353353if (clause -> lisnull )
354354{
355355if (clause -> risnull )
356356{
357- nulleqnull = true;
357+ nulleqnull = true;/* NULL "=" NULL */
358358continue ;
359359}
360- /* NULL > non-NULL */
361- result = 1 ;
360+ if (clause -> nulls_first )
361+ result = -1 ;/* NULL "<" NOT_NULL */
362+ else
363+ result = 1 ;/* NULL ">" NOT_NULL */
362364break ;
363365}
364366if (clause -> risnull )
365367{
366- /* non-NULL < NULL */
367- result = -1 ;
368+ if (clause -> nulls_first )
369+ result = 1 ;/* NOT_NULL ">" NULL */
370+ else
371+ result = -1 ;/* NOT_NULL "<" NULL */
368372break ;
369373}
370374
375+ /*
376+ * OK to call the comparison function.
377+ */
371378InitFunctionCallInfoData (fcinfo ,& (clause -> cmpfinfo ),2 ,
372379NULL ,NULL );
373380fcinfo .arg [0 ]= clause -> ldatum ;
@@ -377,45 +384,16 @@ MJCompare(MergeJoinState *mergestate)
377384fresult = FunctionCallInvoke (& fcinfo );
378385if (fcinfo .isnull )
379386{
380- nulleqnull = true;
381- continue ;
382- }
383- if (DatumGetInt32 (fresult )== 0 )
384- {
385- /* equal */
387+ nulleqnull = true;/* treat like NULL = NULL */
386388continue ;
387389}
388- if (clause -> cmpstrategy == MERGEFUNC_CMP )
389- {
390- if (DatumGetInt32 (fresult )< 0 )
391- {
392- /* less than */
393- result = -1 ;
394- break ;
395- }
396- else
397- {
398- /* greater than */
399- result = 1 ;
400- break ;
401- }
402- }
403- else
404- {
405- /* reverse the sort order */
406- if (DatumGetInt32 (fresult )> 0 )
407- {
408- /* less than */
409- result = -1 ;
410- break ;
411- }
412- else
413- {
414- /* greater than */
415- result = 1 ;
416- break ;
417- }
418- }
390+ result = DatumGetInt32 (fresult );
391+
392+ if (clause -> reverse )
393+ result = - result ;
394+
395+ if (result != 0 )
396+ break ;
419397}
420398
421399/*
@@ -581,7 +559,7 @@ ExecMergeJoin(MergeJoinState *node)
581559List * joinqual ;
582560List * otherqual ;
583561bool qualResult ;
584- int compareResult ;
562+ int32 compareResult ;
585563PlanState * innerPlan ;
586564TupleTableSlot * innerTupleSlot ;
587565PlanState * outerPlan ;