mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Teach tuplestore.c to throw away data before the "mark" point when the caller
is using mark/restore but not rewind or backward-scan capability. Insert a materialize plan node between a mergejoin and its inner child if the inner child is a sort that is expected to spill to disk. The materialize shields the sort from the need to do mark/restore and thereby allows it to perform its final merge pass on-the-fly; while the materialize itself is normally cheap since it won't spill to disk unless the number of tuples with equal key values exceeds work_mem. Greg Stark, with some kibitzing from Tom Lane.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.58 2007/01/05 22:19:28 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.59 2007/05/21 17:57:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -56,10 +56,10 @@ ExecMaterial(MaterialState *node)
|
||||
/*
|
||||
* If first time through, and we need a tuplestore, initialize it.
|
||||
*/
|
||||
if (tuplestorestate == NULL && node->randomAccess)
|
||||
if (tuplestorestate == NULL && node->eflags != 0)
|
||||
{
|
||||
tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
|
||||
|
||||
tuplestore_set_eflags(tuplestorestate, node->eflags);
|
||||
node->tuplestorestate = (void *) tuplestorestate;
|
||||
}
|
||||
|
||||
@ -162,14 +162,14 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
|
||||
matstate->ss.ps.state = estate;
|
||||
|
||||
/*
|
||||
* We must have random access to the subplan output to do backward scan or
|
||||
* mark/restore. We also prefer to materialize the subplan output if we
|
||||
* might be called on to rewind and replay it many times. However, if none
|
||||
* of these cases apply, we can skip storing the data.
|
||||
* We must have a tuplestore buffering the subplan output to do backward
|
||||
* scan or mark/restore. We also prefer to materialize the subplan output
|
||||
* if we might be called on to rewind and replay it many times. However,
|
||||
* if none of these cases apply, we can skip storing the data.
|
||||
*/
|
||||
matstate->randomAccess = (eflags & (EXEC_FLAG_REWIND |
|
||||
EXEC_FLAG_BACKWARD |
|
||||
EXEC_FLAG_MARK)) != 0;
|
||||
matstate->eflags = (eflags & (EXEC_FLAG_REWIND |
|
||||
EXEC_FLAG_BACKWARD |
|
||||
EXEC_FLAG_MARK));
|
||||
|
||||
matstate->eof_underlying = false;
|
||||
matstate->tuplestorestate = NULL;
|
||||
@ -255,7 +255,7 @@ ExecEndMaterial(MaterialState *node)
|
||||
void
|
||||
ExecMaterialMarkPos(MaterialState *node)
|
||||
{
|
||||
Assert(node->randomAccess);
|
||||
Assert(node->eflags & EXEC_FLAG_MARK);
|
||||
|
||||
/*
|
||||
* if we haven't materialized yet, just return.
|
||||
@ -275,7 +275,7 @@ ExecMaterialMarkPos(MaterialState *node)
|
||||
void
|
||||
ExecMaterialRestrPos(MaterialState *node)
|
||||
{
|
||||
Assert(node->randomAccess);
|
||||
Assert(node->eflags & EXEC_FLAG_MARK);
|
||||
|
||||
/*
|
||||
* if we haven't materialized yet, just return.
|
||||
@ -300,7 +300,7 @@ ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
|
||||
{
|
||||
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
||||
|
||||
if (node->randomAccess)
|
||||
if (node->eflags != 0)
|
||||
{
|
||||
/*
|
||||
* If we haven't materialized yet, just return. If outerplan' chgParam
|
||||
@ -312,15 +312,21 @@ ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
|
||||
|
||||
/*
|
||||
* If subnode is to be rescanned then we forget previous stored
|
||||
* results; we have to re-read the subplan and re-store.
|
||||
* results; we have to re-read the subplan and re-store. Also,
|
||||
* if we told tuplestore it needn't support rescan, we lose and
|
||||
* must re-read. (This last should not happen in common cases;
|
||||
* else our caller lied by not passing EXEC_FLAG_REWIND to us.)
|
||||
*
|
||||
* Otherwise we can just rewind and rescan the stored output. The
|
||||
* state of the subnode does not change.
|
||||
*/
|
||||
if (((PlanState *) node)->lefttree->chgParam != NULL)
|
||||
if (((PlanState *) node)->lefttree->chgParam != NULL ||
|
||||
(node->eflags & EXEC_FLAG_REWIND) == 0)
|
||||
{
|
||||
tuplestore_end((Tuplestorestate *) node->tuplestorestate);
|
||||
node->tuplestorestate = NULL;
|
||||
if (((PlanState *) node)->lefttree->chgParam == NULL)
|
||||
ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
|
||||
node->eof_underlying = false;
|
||||
}
|
||||
else
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.87 2007/02/02 00:07:03 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.88 2007/05/21 17:57:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -706,6 +706,9 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Mark before advancing, if wanted */
|
||||
if (node->mj_ExtraMarks)
|
||||
ExecMarkPos(innerPlan);
|
||||
/* Stay in same state to fetch next inner tuple */
|
||||
if (doFillInner)
|
||||
{
|
||||
@ -830,6 +833,9 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
* now we get the next inner tuple, if any. If there's none,
|
||||
* advance to next outer tuple (which may be able to join to
|
||||
* previously marked tuples).
|
||||
*
|
||||
* NB: must NOT do "extraMarks" here, since we may need to
|
||||
* return to previously marked tuples.
|
||||
*/
|
||||
innerTupleSlot = ExecProcNode(innerPlan);
|
||||
node->mj_InnerTupleSlot = innerTupleSlot;
|
||||
@ -1140,6 +1146,9 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
break;
|
||||
|
||||
/*
|
||||
* SKIPOUTER_ADVANCE: advance over an outer tuple that is
|
||||
* known not to join to any inner tuple.
|
||||
*
|
||||
* Before advancing, we check to see if we must emit an
|
||||
* outer-join fill tuple for this outer tuple.
|
||||
*/
|
||||
@ -1204,6 +1213,9 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
break;
|
||||
|
||||
/*
|
||||
* SKIPINNER_ADVANCE: advance over an inner tuple that is
|
||||
* known not to join to any outer tuple.
|
||||
*
|
||||
* Before advancing, we check to see if we must emit an
|
||||
* outer-join fill tuple for this inner tuple.
|
||||
*/
|
||||
@ -1225,6 +1237,10 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Mark before advancing, if wanted */
|
||||
if (node->mj_ExtraMarks)
|
||||
ExecMarkPos(innerPlan);
|
||||
|
||||
/*
|
||||
* now we get the next inner tuple, if any
|
||||
*/
|
||||
@ -1295,6 +1311,10 @@ ExecMergeJoin(MergeJoinState *node)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Mark before advancing, if wanted */
|
||||
if (node->mj_ExtraMarks)
|
||||
ExecMarkPos(innerPlan);
|
||||
|
||||
/*
|
||||
* now we get the next inner tuple, if any
|
||||
*/
|
||||
@ -1425,6 +1445,22 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
|
||||
innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate,
|
||||
eflags | EXEC_FLAG_MARK);
|
||||
|
||||
/*
|
||||
* For certain types of inner child nodes, it is advantageous to issue
|
||||
* MARK every time we advance past an inner tuple we will never return
|
||||
* to. For other types, MARK on a tuple we cannot return to is a waste
|
||||
* of cycles. Detect which case applies and set mj_ExtraMarks if we
|
||||
* want to issue "unnecessary" MARK calls.
|
||||
*
|
||||
* Currently, only Material wants the extra MARKs, and it will be helpful
|
||||
* only if eflags doesn't specify REWIND.
|
||||
*/
|
||||
if (IsA(innerPlan(node), Material) &&
|
||||
(eflags & EXEC_FLAG_REWIND) == 0)
|
||||
mergestate->mj_ExtraMarks = true;
|
||||
else
|
||||
mergestate->mj_ExtraMarks = false;
|
||||
|
||||
#define MERGEJOIN_NSLOTS 4
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user