diff options
Diffstat (limited to 'src/backend/executor/nodeMergejoin.c')
-rw-r--r-- | src/backend/executor/nodeMergejoin.c | 255 |
1 files changed, 84 insertions, 171 deletions
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 8a9f6fe2300..97824920d63 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.82 2006/10/04 00:29:52 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.83 2006/12/23 00:43:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,14 +106,14 @@ /* * Comparison strategies supported by MJCompare * - * XXX eventually should extend these to support descending-order sorts. + * 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. */ typedef enum { - MERGEFUNC_LT, /* raw "<" operator */ - MERGEFUNC_CMP /* -1 / 0 / 1 three-way comparator */ + 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 */ @@ -133,19 +133,10 @@ typedef struct MergeJoinClauseData bool risnull; /* - * Remember whether mergejoin operator is strict (usually it will be). - * NOTE: if it's not strict, we still assume it cannot return true for one - * null and one non-null input. - */ - bool mergestrict; - - /* * The comparison strategy in use, and the lookup info to let us call the - * needed comparison routines. eqfinfo is the "=" operator itself. - * cmpfinfo is either the btree comparator or the "<" operator. + * btree comparison support function. */ MergeFunctionKind cmpstrategy; - FmgrInfo eqfinfo; FmgrInfo cmpfinfo; } MergeJoinClauseData; @@ -164,34 +155,51 @@ typedef struct MergeJoinClauseData * we will need at runtime. Each struct essentially tells us how to compare * the two expressions from the original clause. * - * The best, most efficient way to compare two expressions is to use a btree - * comparison support routine, since that requires only one function call - * per comparison. Hence we try to find a btree opclass that matches the - * mergejoinable operator. If we cannot find one, we'll have to call both - * the "=" and (often) the "<" operator for each comparison. + * In addition to the expressions themselves, the planner passes the btree + * opfamily OID and btree strategy number (BTLessStrategyNumber or + * BTGreaterStrategyNumber) that identify the intended merge semantics 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. */ static MergeJoinClause -MJExamineQuals(List *qualList, PlanState *parent) +MJExamineQuals(List *mergeclauses, List *mergefamilies, List *mergestrategies, + PlanState *parent) { MergeJoinClause clauses; - int nClauses = list_length(qualList); + int nClauses = list_length(mergeclauses); int iClause; - ListCell *l; + ListCell *cl; + ListCell *cf; + ListCell *cs; clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData)); iClause = 0; - foreach(l, qualList) + cf = list_head(mergefamilies); + cs = list_head(mergestrategies); + foreach(cl, mergeclauses) { - OpExpr *qual = (OpExpr *) lfirst(l); + OpExpr *qual = (OpExpr *) lfirst(cl); MergeJoinClause clause = &clauses[iClause]; - Oid ltop; - Oid gtop; - RegProcedure ltproc; - RegProcedure gtproc; + Oid opfamily; + StrategyNumber opstrategy; + int op_strategy; + Oid op_lefttype; + Oid op_righttype; + bool op_recheck; + RegProcedure cmpproc; AclResult aclresult; - CatCList *catlist; - int i; + + opfamily = lfirst_oid(cf); + cf = lnext(cf); + opstrategy = lfirst_int(cs); + cs = lnext(cs); + + /* Later we'll support both ascending and descending sort... */ + Assert(opstrategy == BTLessStrategyNumber); + clause->cmpstrategy = MERGEFUNC_CMP; if (!IsA(qual, OpExpr)) elog(ERROR, "mergejoin clause is not an OpExpr"); @@ -202,77 +210,30 @@ MJExamineQuals(List *qualList, PlanState *parent) clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent); clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent); - /* - * Check permission to call the mergejoinable operator. For - * predictability, we check this even if we end up not using it. - */ - aclresult = pg_proc_aclcheck(qual->opfuncid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(qual->opfuncid)); - - /* Set up the fmgr lookup information */ - fmgr_info(qual->opfuncid, &(clause->eqfinfo)); - - /* And remember strictness */ - clause->mergestrict = clause->eqfinfo.fn_strict; - - /* - * Lookup the comparison operators that go with the mergejoinable - * top-level operator. (This will elog if the operator isn't - * mergejoinable, which would be the planner's mistake.) - */ - op_mergejoin_crossops(qual->opno, - <op, - >op, - <proc, - >proc); - - clause->cmpstrategy = MERGEFUNC_LT; - - /* - * Look for a btree opclass including all three operators. This is - * much like SelectSortFunction except we insist on matching all the - * operators provided, and it can be a cross-type opclass. - * - * XXX for now, insist on forward sort so that NULLs can be counted on - * to be high. - */ - catlist = SearchSysCacheList(AMOPOPID, 1, - ObjectIdGetDatum(qual->opno), - 0, 0, 0); - - for (i = 0; i < catlist->n_members; i++) - { - HeapTuple tuple = &catlist->members[i]->tuple; - Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple); - Oid opcid = aform->amopclaid; - - if (aform->amopstrategy != BTEqualStrategyNumber) - continue; - if (!opclass_is_btree(opcid)) - continue; - if (get_op_opclass_strategy(ltop, opcid) == BTLessStrategyNumber && - get_op_opclass_strategy(gtop, opcid) == BTGreaterStrategyNumber) - { - clause->cmpstrategy = MERGEFUNC_CMP; - ltproc = get_opclass_proc(opcid, aform->amopsubtype, - BTORDER_PROC); - Assert(RegProcedureIsValid(ltproc)); - break; /* done looking */ - } - } - - ReleaseSysCacheList(catlist); - - /* Check permission to call "<" operator or cmp function */ - aclresult = pg_proc_aclcheck(ltproc, GetUserId(), ACL_EXECUTE); + /* Extract the operator's declared left/right datatypes */ + get_op_opfamily_properties(qual->opno, opfamily, + &op_strategy, + &op_lefttype, + &op_righttype, + &op_recheck); + Assert(op_strategy == BTEqualStrategyNumber); + Assert(!op_recheck); + + /* And get the matching support procedure (comparison function) */ + cmpproc = get_opfamily_proc(opfamily, + op_lefttype, + op_righttype, + BTORDER_PROC); + Assert(RegProcedureIsValid(cmpproc)); + + /* Check permission to call cmp function */ + aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(ltproc)); + get_func_name(cmpproc)); /* Set up the fmgr lookup information */ - fmgr_info(ltproc, &(clause->cmpfinfo)); + fmgr_info(cmpproc, &(clause->cmpfinfo)); iClause++; } @@ -286,7 +247,7 @@ MJExamineQuals(List *qualList, PlanState *parent) * Compute the values of the mergejoined expressions for the current * outer tuple. We also detect whether it's impossible for the current * outer tuple to match anything --- this is true if it yields a NULL - * input for any strict mergejoin operator. + * input, since we assume mergejoin operators are strict. * * We evaluate the values in OuterEContext, which can be reset each * time we move to a new tuple. @@ -311,7 +272,7 @@ MJEvalOuterValues(MergeJoinState *mergestate) clause->ldatum = ExecEvalExpr(clause->lexpr, econtext, &clause->lisnull, NULL); - if (clause->lisnull && clause->mergestrict) + if (clause->lisnull) canmatch = false; } @@ -347,7 +308,7 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot) clause->rdatum = ExecEvalExpr(clause->rexpr, econtext, &clause->risnull, NULL); - if (clause->risnull && clause->mergestrict) + if (clause->risnull) canmatch = false; } @@ -391,32 +352,11 @@ MJCompare(MergeJoinState *mergestate) /* * Deal with null inputs. We treat NULL as sorting after non-NULL. - * - * If both inputs are NULL, and the comparison function isn't strict, - * then we call it and check for a true result (this allows operators - * that behave like IS NOT DISTINCT to be mergejoinable). If the - * function is strict or returns false, we temporarily pretend NULL == - * NULL and contine checking remaining columns. */ if (clause->lisnull) { if (clause->risnull) { - if (!clause->eqfinfo.fn_strict) - { - InitFunctionCallInfoData(fcinfo, &(clause->eqfinfo), 2, - NULL, NULL); - fcinfo.arg[0] = clause->ldatum; - fcinfo.arg[1] = clause->rdatum; - fcinfo.argnull[0] = true; - fcinfo.argnull[1] = true; - fresult = FunctionCallInvoke(&fcinfo); - if (!fcinfo.isnull && DatumGetBool(fresult)) - { - /* treat nulls as really equal */ - continue; - } - } nulleqnull = true; continue; } @@ -431,38 +371,26 @@ MJCompare(MergeJoinState *mergestate) break; } - if (clause->cmpstrategy == MERGEFUNC_LT) + InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, + NULL, NULL); + fcinfo.arg[0] = clause->ldatum; + fcinfo.arg[1] = clause->rdatum; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + fresult = FunctionCallInvoke(&fcinfo); + if (fcinfo.isnull) { - InitFunctionCallInfoData(fcinfo, &(clause->eqfinfo), 2, - NULL, NULL); - fcinfo.arg[0] = clause->ldatum; - fcinfo.arg[1] = clause->rdatum; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fresult = FunctionCallInvoke(&fcinfo); - if (fcinfo.isnull) - { - nulleqnull = true; - continue; - } - else if (DatumGetBool(fresult)) - { - /* equal */ - continue; - } - InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, - NULL, NULL); - fcinfo.arg[0] = clause->ldatum; - fcinfo.arg[1] = clause->rdatum; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fresult = FunctionCallInvoke(&fcinfo); - if (fcinfo.isnull) - { - nulleqnull = true; - continue; - } - else if (DatumGetBool(fresult)) + nulleqnull = true; + continue; + } + if (DatumGetInt32(fresult) == 0) + { + /* equal */ + continue; + } + if (clause->cmpstrategy == MERGEFUNC_CMP) + { + if (DatumGetInt32(fresult) < 0) { /* less than */ result = -1; @@ -476,26 +404,9 @@ MJCompare(MergeJoinState *mergestate) } } else - /* must be MERGEFUNC_CMP */ { - InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2, - NULL, NULL); - fcinfo.arg[0] = clause->ldatum; - fcinfo.arg[1] = clause->rdatum; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fresult = FunctionCallInvoke(&fcinfo); - if (fcinfo.isnull) - { - nulleqnull = true; - continue; - } - else if (DatumGetInt32(fresult) == 0) - { - /* equal */ - continue; - } - else if (DatumGetInt32(fresult) < 0) + /* reverse the sort order */ + if (DatumGetInt32(fresult) > 0) { /* less than */ result = -1; @@ -1614,6 +1525,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) */ mergestate->mj_NumClauses = list_length(node->mergeclauses); mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses, + node->mergefamilies, + node->mergestrategies, (PlanState *) mergestate); /* |