From ad429fe314d3dd42dc3f3e827f4ffad2b121a1ea Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 11 Jan 2007 17:19:13 +0000 Subject: [PATCH] Teach nodeMergejoin how to handle DESC and/or NULLS FIRST sort orders. So far only tested by hacking the planner ... --- src/backend/executor/nodeMergejoin.c | 132 +++++++++++++++-------------------- 1 file changed, 55 insertions(+), 77 deletions(-) diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index a5f08c28ef..6e820d7ad2 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.85 2007/01/10 18:06:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.86 2007/01/11 17:19:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -39,12 +39,13 @@ * therefore it should scan the outer relation first to find a * matching tuple and so on. * - * Therefore, when initializing the merge-join node, we look up the - * associated sort operators. We assume the planner has seen to it - * that the inputs are correctly sorted by these operators. Rather - * than directly executing the merge join clauses, we evaluate the - * left and right key expressions separately and then compare the - * columns one at a time (see MJCompare). + * Therefore, rather than directly executing the merge join clauses, + * we evaluate the left and right key expressions separately and then + * compare the columns one at a time (see MJCompare). The planner + * passes us enough information about the sort ordering of the inputs + * to allow us to determine how to make the comparison. We may use the + * appropriate btree comparison function, since Postgres' only notion + * of ordering is specified by btree opfamilies. * * * Consider the above relations and suppose that the executor has @@ -104,19 +105,8 @@ /* - * Comparison strategies supported by MJCompare - * - * XXX eventually should extend MJCompare to support descending-order sorts. - * There are some tricky issues however about being sure we are on the same - * page as the underlying sort or index as to which end NULLs sort to. + * Runtime data for each mergejoin clause */ -typedef enum -{ - MERGEFUNC_CMP, /* -1 / 0 / 1 three-way comparator */ - MERGEFUNC_REV_CMP /* same, reversing the sense of the result */ -} MergeFunctionKind; - -/* Runtime data for each mergejoin clause */ typedef struct MergeJoinClauseData { /* Executable expression trees */ @@ -136,7 +126,8 @@ typedef struct MergeJoinClauseData * The comparison strategy in use, and the lookup info to let us call the * btree comparison support function. */ - MergeFunctionKind cmpstrategy; + bool reverse; /* if true, negate the cmpfn's output */ + bool nulls_first; /* if true, nulls sort low */ FmgrInfo cmpfinfo; } MergeJoinClauseData; @@ -158,11 +149,11 @@ typedef struct MergeJoinClauseData * In addition to the expressions themselves, the planner passes the btree * opfamily OID, btree strategy number (BTLessStrategyNumber or * BTGreaterStrategyNumber), and nulls-first flag that identify the intended - * merge semantics for each merge key. The mergejoinable operator is an + * sort ordering for each merge key. The mergejoinable operator is an * equality operator in this opfamily, and the two inputs are guaranteed to be * ordered in either increasing or decreasing (respectively) order according - * to this opfamily. This allows us to obtain the needed comparison functions - * from the opfamily. + * to this opfamily, with nulls at the indicated end of the range. This + * allows us to obtain the needed comparison function from the opfamily. */ static MergeJoinClause MJExamineQuals(List *mergeclauses, @@ -193,11 +184,6 @@ MJExamineQuals(List *mergeclauses, RegProcedure cmpproc; AclResult aclresult; - /* Later we'll support both ascending and descending sort... */ - Assert(opstrategy == BTLessStrategyNumber); - clause->cmpstrategy = MERGEFUNC_CMP; - Assert(!nulls_first); - if (!IsA(qual, OpExpr)) elog(ERROR, "mergejoin clause is not an OpExpr"); @@ -213,15 +199,19 @@ MJExamineQuals(List *mergeclauses, &op_lefttype, &op_righttype, &op_recheck); - Assert(op_strategy == BTEqualStrategyNumber); - Assert(!op_recheck); + if (op_strategy != BTEqualStrategyNumber) /* should not happen */ + elog(ERROR, "cannot merge using non-equality operator %u", + qual->opno); + Assert(!op_recheck); /* never true for btree */ /* And get the matching support procedure (comparison function) */ cmpproc = get_opfamily_proc(opfamily, op_lefttype, op_righttype, BTORDER_PROC); - Assert(RegProcedureIsValid(cmpproc)); + if (!RegProcedureIsValid(cmpproc)) /* should not happen */ + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + BTORDER_PROC, op_lefttype, op_righttype, opfamily); /* Check permission to call cmp function */ aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE); @@ -232,6 +222,16 @@ MJExamineQuals(List *mergeclauses, /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); + /* Fill the additional comparison-strategy flags */ + if (opstrategy == BTLessStrategyNumber) + clause->reverse = false; + else if (opstrategy == BTGreaterStrategyNumber) + clause->reverse = true; + else /* planner screwed up */ + elog(ERROR, "unsupported mergejoin strategy %d", opstrategy); + + clause->nulls_first = nulls_first; + iClause++; } @@ -324,10 +324,10 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot) * MJEvalOuterValues and MJEvalInnerValues must already have been called * for the current outer and inner tuples, respectively. */ -static int +static int32 MJCompare(MergeJoinState *mergestate) { - int result = 0; + int32 result = 0; bool nulleqnull = false; ExprContext *econtext = mergestate->js.ps.ps_ExprContext; int i; @@ -348,26 +348,33 @@ MJCompare(MergeJoinState *mergestate) Datum fresult; /* - * Deal with null inputs. We treat NULL as sorting after non-NULL. + * Deal with null inputs. */ if (clause->lisnull) { if (clause->risnull) { - nulleqnull = true; + nulleqnull = true; /* NULL "=" NULL */ continue; } - /* NULL > non-NULL */ - result = 1; + if (clause->nulls_first) + result = -1; /* NULL "<" NOT_NULL */ + else + result = 1; /* NULL ">" NOT_NULL */ break; } if (clause->risnull) { - /* non-NULL < NULL */ - result = -1; + if (clause->nulls_first) + result = 1; /* NOT_NULL ">" NULL */ + else + result = -1; /* NOT_NULL "<" NULL */ break; } + /* + * OK to call the comparison function. + */ InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, NULL, NULL); fcinfo.arg[0] = clause->ldatum; @@ -377,45 +384,16 @@ MJCompare(MergeJoinState *mergestate) fresult = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) { - nulleqnull = true; - continue; - } - if (DatumGetInt32(fresult) == 0) - { - /* equal */ + nulleqnull = true; /* treat like NULL = NULL */ continue; } - if (clause->cmpstrategy == MERGEFUNC_CMP) - { - if (DatumGetInt32(fresult) < 0) - { - /* less than */ - result = -1; - break; - } - else - { - /* greater than */ - result = 1; - break; - } - } - else - { - /* reverse the sort order */ - if (DatumGetInt32(fresult) > 0) - { - /* less than */ - result = -1; - break; - } - else - { - /* greater than */ - result = 1; - break; - } - } + result = DatumGetInt32(fresult); + + if (clause->reverse) + result = -result; + + if (result != 0) + break; } /* @@ -581,7 +559,7 @@ ExecMergeJoin(MergeJoinState *node) List *joinqual; List *otherqual; bool qualResult; - int compareResult; + int32 compareResult; PlanState *innerPlan; TupleTableSlot *innerTupleSlot; PlanState *outerPlan; -- 2.11.0