Teach nodeMergejoin how to handle DESC and/or NULLS FIRST sort orders.
So far only tested by hacking the planner ...
This commit is contained in:
parent
5b88b85cad
commit
ad429fe314
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* therefore it should scan the outer relation first to find a
|
||||||
* matching tuple and so on.
|
* matching tuple and so on.
|
||||||
*
|
*
|
||||||
* Therefore, when initializing the merge-join node, we look up the
|
* Therefore, rather than directly executing the merge join clauses,
|
||||||
* associated sort operators. We assume the planner has seen to it
|
* we evaluate the left and right key expressions separately and then
|
||||||
* that the inputs are correctly sorted by these operators. Rather
|
* compare the columns one at a time (see MJCompare). The planner
|
||||||
* than directly executing the merge join clauses, we evaluate the
|
* passes us enough information about the sort ordering of the inputs
|
||||||
* left and right key expressions separately and then compare the
|
* to allow us to determine how to make the comparison. We may use the
|
||||||
* columns one at a time (see MJCompare).
|
* 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
|
* Consider the above relations and suppose that the executor has
|
||||||
@ -104,19 +105,8 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Comparison strategies supported by MJCompare
|
* Runtime data for each mergejoin clause
|
||||||
*
|
|
||||||
* 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_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
|
typedef struct MergeJoinClauseData
|
||||||
{
|
{
|
||||||
/* Executable expression trees */
|
/* Executable expression trees */
|
||||||
@ -136,7 +126,8 @@ typedef struct MergeJoinClauseData
|
|||||||
* The comparison strategy in use, and the lookup info to let us call the
|
* The comparison strategy in use, and the lookup info to let us call the
|
||||||
* btree comparison support function.
|
* 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;
|
FmgrInfo cmpfinfo;
|
||||||
} MergeJoinClauseData;
|
} MergeJoinClauseData;
|
||||||
|
|
||||||
@ -158,11 +149,11 @@ typedef struct MergeJoinClauseData
|
|||||||
* In addition to the expressions themselves, the planner passes the btree
|
* In addition to the expressions themselves, the planner passes the btree
|
||||||
* opfamily OID, btree strategy number (BTLessStrategyNumber or
|
* opfamily OID, btree strategy number (BTLessStrategyNumber or
|
||||||
* BTGreaterStrategyNumber), and nulls-first flag that identify the intended
|
* 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
|
* equality operator in this opfamily, and the two inputs are guaranteed to be
|
||||||
* ordered in either increasing or decreasing (respectively) order according
|
* ordered in either increasing or decreasing (respectively) order according
|
||||||
* to this opfamily. This allows us to obtain the needed comparison functions
|
* to this opfamily, with nulls at the indicated end of the range. This
|
||||||
* from the opfamily.
|
* allows us to obtain the needed comparison function from the opfamily.
|
||||||
*/
|
*/
|
||||||
static MergeJoinClause
|
static MergeJoinClause
|
||||||
MJExamineQuals(List *mergeclauses,
|
MJExamineQuals(List *mergeclauses,
|
||||||
@ -193,11 +184,6 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
RegProcedure cmpproc;
|
RegProcedure cmpproc;
|
||||||
AclResult aclresult;
|
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))
|
if (!IsA(qual, OpExpr))
|
||||||
elog(ERROR, "mergejoin clause is not an OpExpr");
|
elog(ERROR, "mergejoin clause is not an OpExpr");
|
||||||
|
|
||||||
@ -213,15 +199,19 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
&op_lefttype,
|
&op_lefttype,
|
||||||
&op_righttype,
|
&op_righttype,
|
||||||
&op_recheck);
|
&op_recheck);
|
||||||
Assert(op_strategy == BTEqualStrategyNumber);
|
if (op_strategy != BTEqualStrategyNumber) /* should not happen */
|
||||||
Assert(!op_recheck);
|
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) */
|
/* And get the matching support procedure (comparison function) */
|
||||||
cmpproc = get_opfamily_proc(opfamily,
|
cmpproc = get_opfamily_proc(opfamily,
|
||||||
op_lefttype,
|
op_lefttype,
|
||||||
op_righttype,
|
op_righttype,
|
||||||
BTORDER_PROC);
|
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 */
|
/* Check permission to call cmp function */
|
||||||
aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE);
|
aclresult = pg_proc_aclcheck(cmpproc, GetUserId(), ACL_EXECUTE);
|
||||||
@ -232,6 +222,16 @@ MJExamineQuals(List *mergeclauses,
|
|||||||
/* Set up the fmgr lookup information */
|
/* Set up the fmgr lookup information */
|
||||||
fmgr_info(cmpproc, &(clause->cmpfinfo));
|
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++;
|
iClause++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,10 +324,10 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
|
|||||||
* MJEvalOuterValues and MJEvalInnerValues must already have been called
|
* MJEvalOuterValues and MJEvalInnerValues must already have been called
|
||||||
* for the current outer and inner tuples, respectively.
|
* for the current outer and inner tuples, respectively.
|
||||||
*/
|
*/
|
||||||
static int
|
static int32
|
||||||
MJCompare(MergeJoinState *mergestate)
|
MJCompare(MergeJoinState *mergestate)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int32 result = 0;
|
||||||
bool nulleqnull = false;
|
bool nulleqnull = false;
|
||||||
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
|
ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
|
||||||
int i;
|
int i;
|
||||||
@ -348,26 +348,33 @@ MJCompare(MergeJoinState *mergestate)
|
|||||||
Datum fresult;
|
Datum fresult;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deal with null inputs. We treat NULL as sorting after non-NULL.
|
* Deal with null inputs.
|
||||||
*/
|
*/
|
||||||
if (clause->lisnull)
|
if (clause->lisnull)
|
||||||
{
|
{
|
||||||
if (clause->risnull)
|
if (clause->risnull)
|
||||||
{
|
{
|
||||||
nulleqnull = true;
|
nulleqnull = true; /* NULL "=" NULL */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* NULL > non-NULL */
|
if (clause->nulls_first)
|
||||||
result = 1;
|
result = -1; /* NULL "<" NOT_NULL */
|
||||||
|
else
|
||||||
|
result = 1; /* NULL ">" NOT_NULL */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (clause->risnull)
|
if (clause->risnull)
|
||||||
{
|
{
|
||||||
/* non-NULL < NULL */
|
if (clause->nulls_first)
|
||||||
result = -1;
|
result = 1; /* NOT_NULL ">" NULL */
|
||||||
|
else
|
||||||
|
result = -1; /* NOT_NULL "<" NULL */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK to call the comparison function.
|
||||||
|
*/
|
||||||
InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2,
|
InitFunctionCallInfoData(fcinfo, &(clause->cmpfinfo), 2,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
fcinfo.arg[0] = clause->ldatum;
|
fcinfo.arg[0] = clause->ldatum;
|
||||||
@ -377,45 +384,16 @@ MJCompare(MergeJoinState *mergestate)
|
|||||||
fresult = FunctionCallInvoke(&fcinfo);
|
fresult = FunctionCallInvoke(&fcinfo);
|
||||||
if (fcinfo.isnull)
|
if (fcinfo.isnull)
|
||||||
{
|
{
|
||||||
nulleqnull = true;
|
nulleqnull = true; /* treat like NULL = NULL */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (DatumGetInt32(fresult) == 0)
|
result = DatumGetInt32(fresult);
|
||||||
{
|
|
||||||
/* equal */
|
if (clause->reverse)
|
||||||
continue;
|
result = -result;
|
||||||
}
|
|
||||||
if (clause->cmpstrategy == MERGEFUNC_CMP)
|
if (result != 0)
|
||||||
{
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -581,7 +559,7 @@ ExecMergeJoin(MergeJoinState *node)
|
|||||||
List *joinqual;
|
List *joinqual;
|
||||||
List *otherqual;
|
List *otherqual;
|
||||||
bool qualResult;
|
bool qualResult;
|
||||||
int compareResult;
|
int32 compareResult;
|
||||||
PlanState *innerPlan;
|
PlanState *innerPlan;
|
||||||
TupleTableSlot *innerTupleSlot;
|
TupleTableSlot *innerTupleSlot;
|
||||||
PlanState *outerPlan;
|
PlanState *outerPlan;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user