Commit d8b1bf47 authored by Tom Lane's avatar Tom Lane

Create a new 'MultiExecProcNode' call API for plan nodes that don't

return just a single tuple at a time.  Currently the only such node
type is Hash, but I expect we will soon have indexscans that can return
tuple bitmaps.  A side benefit is that EXPLAIN ANALYZE now shows the
correct tuple count for a Hash node.
parent 85eee28c
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.131 2005/03/25 21:57:58 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.132 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,7 +47,7 @@ typedef struct ExplainState ...@@ -47,7 +47,7 @@ typedef struct ExplainState
static void ExplainOneQuery(Query *query, ExplainStmt *stmt, static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
TupOutputState *tstate); TupOutputState *tstate);
static double elapsed_time(instr_time * starttime); static double elapsed_time(instr_time *starttime);
static void explain_outNode(StringInfo str, static void explain_outNode(StringInfo str,
Plan *plan, PlanState *planstate, Plan *plan, PlanState *planstate,
Plan *outer_plan, Plan *outer_plan,
...@@ -296,7 +296,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, ...@@ -296,7 +296,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
{ {
int nt; int nt;
if (!rInfo->ri_TrigDesc) if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
continue; continue;
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++) for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
{ {
...@@ -366,7 +366,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, ...@@ -366,7 +366,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
/* Compute elapsed time in seconds since given timestamp */ /* Compute elapsed time in seconds since given timestamp */
static double static double
elapsed_time(instr_time * starttime) elapsed_time(instr_time *starttime)
{ {
instr_time endtime; instr_time endtime;
...@@ -663,7 +663,8 @@ explain_outNode(StringInfo str, ...@@ -663,7 +663,8 @@ explain_outNode(StringInfo str,
* We have to forcibly clean up the instrumentation state because * We have to forcibly clean up the instrumentation state because
* we haven't done ExecutorEnd yet. This is pretty grotty ... * we haven't done ExecutorEnd yet. This is pretty grotty ...
*/ */
InstrEndLoop(planstate->instrument); if (planstate->instrument)
InstrEndLoop(planstate->instrument);
if (planstate->instrument && planstate->instrument->nloops > 0) if (planstate->instrument && planstate->instrument->nloops > 0)
{ {
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.48 2005/04/06 20:13:49 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.49 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -375,6 +375,50 @@ ExecProcNode(PlanState *node) ...@@ -375,6 +375,50 @@ ExecProcNode(PlanState *node)
return result; return result;
} }
/* ----------------------------------------------------------------
* MultiExecProcNode
*
* Execute a node that doesn't return individual tuples
* (it might return a hashtable, bitmap, etc). Caller should
* check it got back the expected kind of Node.
*
* This has essentially the same responsibilities as ExecProcNode,
* but it does not do InstrStartNode/InstrStopNode (mainly because
* it can't tell how many returned tuples to count). Each per-node
* function must provide its own instrumentation support.
* ----------------------------------------------------------------
*/
Node *
MultiExecProcNode(PlanState *node)
{
Node *result;
CHECK_FOR_INTERRUPTS();
if (node->chgParam != NULL) /* something changed */
ExecReScan(node, NULL); /* let ReScan handle this */
switch (nodeTag(node))
{
/*
* Only node types that actually support multiexec will be listed
*/
case T_HashState:
result = MultiExecHash((HashState *) node);
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
result = NULL;
break;
}
return result;
}
/* /*
* ExecCountSlotsNode - count up the number of tuple table slots needed * ExecCountSlotsNode - count up the number of tuple table slots needed
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.11 2005/03/25 21:57:58 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/instrument.c,v 1.12 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -33,13 +33,10 @@ InstrAlloc(int n) ...@@ -33,13 +33,10 @@ InstrAlloc(int n)
void void
InstrStartNode(Instrumentation *instr) InstrStartNode(Instrumentation *instr)
{ {
if (!instr) if (INSTR_TIME_IS_ZERO(instr->starttime))
return;
if (!INSTR_TIME_IS_ZERO(instr->starttime))
elog(DEBUG2, "InstrStartNode called twice in a row");
else
INSTR_TIME_SET_CURRENT(instr->starttime); INSTR_TIME_SET_CURRENT(instr->starttime);
else
elog(DEBUG2, "InstrStartNode called twice in a row");
} }
/* Exit from a plan node */ /* Exit from a plan node */
...@@ -48,12 +45,13 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple) ...@@ -48,12 +45,13 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple)
{ {
instr_time endtime; instr_time endtime;
if (!instr) /* count the returned tuples */
return; if (returnedTuple)
instr->tuplecount += 1;
if (INSTR_TIME_IS_ZERO(instr->starttime)) if (INSTR_TIME_IS_ZERO(instr->starttime))
{ {
elog(DEBUG2, "InstrStopNode without start"); elog(DEBUG2, "InstrStopNode called without start");
return; return;
} }
...@@ -86,9 +84,17 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple) ...@@ -86,9 +84,17 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple)
instr->running = true; instr->running = true;
instr->firsttuple = INSTR_TIME_GET_DOUBLE(instr->counter); instr->firsttuple = INSTR_TIME_GET_DOUBLE(instr->counter);
} }
}
if (returnedTuple) /* As above, but count multiple tuples returned at once */
instr->tuplecount += 1; void
InstrStopNodeMulti(Instrumentation *instr, double nTuples)
{
/* count the returned tuples */
instr->tuplecount += nTuples;
/* delegate the rest */
InstrStopNode(instr, false);
} }
/* Finish a run cycle for a plan node */ /* Finish a run cycle for a plan node */
...@@ -97,14 +103,14 @@ InstrEndLoop(Instrumentation *instr) ...@@ -97,14 +103,14 @@ InstrEndLoop(Instrumentation *instr)
{ {
double totaltime; double totaltime;
if (!instr)
return;
/* Skip if nothing has happened, or already shut down */ /* Skip if nothing has happened, or already shut down */
if (!instr->running) if (!instr->running)
return; return;
/* Accumulate statistics */ if (!INSTR_TIME_IS_ZERO(instr->starttime))
elog(DEBUG2, "InstrEndLoop called on running node");
/* Accumulate per-cycle statistics into totals */
totaltime = INSTR_TIME_GET_DOUBLE(instr->counter); totaltime = INSTR_TIME_GET_DOUBLE(instr->counter);
instr->startup += instr->firsttuple; instr->startup += instr->firsttuple;
......
...@@ -8,13 +8,13 @@ ...@@ -8,13 +8,13 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.92 2005/03/31 02:02:52 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.93 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* /*
* INTERFACE ROUTINES * INTERFACE ROUTINES
* ExecHash - generate an in-memory hash table of the relation * MultiExecHash - generate an in-memory hash table of the relation
* ExecInitHash - initialize node and subnodes * ExecInitHash - initialize node and subnodes
* ExecEndHash - shutdown node and subnodes * ExecEndHash - shutdown node and subnodes
*/ */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "executor/hashjoin.h" #include "executor/hashjoin.h"
#include "executor/instrument.h"
#include "executor/nodeHash.h" #include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h" #include "executor/nodeHashjoin.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -36,12 +37,25 @@ static void ExecHashIncreaseNumBatches(HashJoinTable hashtable); ...@@ -36,12 +37,25 @@ static void ExecHashIncreaseNumBatches(HashJoinTable hashtable);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecHash * ExecHash
* *
* build hash table for hashjoin, doing partitioning if more * stub for pro forma compliance
* than one batch is required.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
TupleTableSlot * TupleTableSlot *
ExecHash(HashState *node) ExecHash(HashState *node)
{
elog(ERROR, "Hash node does not support ExecProcNode call convention");
return NULL;
}
/* ----------------------------------------------------------------
* MultiExecHash
*
* build hash table for hashjoin, doing partitioning if more
* than one batch is required.
* ----------------------------------------------------------------
*/
Node *
MultiExecHash(HashState *node)
{ {
PlanState *outerNode; PlanState *outerNode;
List *hashkeys; List *hashkeys;
...@@ -50,6 +64,10 @@ ExecHash(HashState *node) ...@@ -50,6 +64,10 @@ ExecHash(HashState *node)
ExprContext *econtext; ExprContext *econtext;
uint32 hashvalue; uint32 hashvalue;
/* must provide our own instrumentation support */
if (node->ps.instrument)
InstrStartNode(node->ps.instrument);
/* /*
* get state info from node * get state info from node
*/ */
...@@ -70,14 +88,24 @@ ExecHash(HashState *node) ...@@ -70,14 +88,24 @@ ExecHash(HashState *node)
slot = ExecProcNode(outerNode); slot = ExecProcNode(outerNode);
if (TupIsNull(slot)) if (TupIsNull(slot))
break; break;
hashtable->hashNonEmpty = true; hashtable->totalTuples += 1;
/* We have to compute the hash value */ /* We have to compute the hash value */
econtext->ecxt_innertuple = slot; econtext->ecxt_innertuple = slot;
hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys); hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys);
ExecHashTableInsert(hashtable, ExecFetchSlotTuple(slot), hashvalue); ExecHashTableInsert(hashtable, ExecFetchSlotTuple(slot), hashvalue);
} }
/* We needn't return a tuple slot or anything else */ /* must provide our own instrumentation support */
if (node->ps.instrument)
InstrStopNodeMulti(node->ps.instrument, hashtable->totalTuples);
/*
* We do not return the hash table directly because it's not a subtype
* of Node, and so would violate the MultiExecProcNode API. Instead,
* our parent Hashjoin node is expected to know how to fish it out
* of our node state. Ugly but not really worth cleaning up, since
* Hashjoin knows quite a bit more about Hash besides that.
*/
return NULL; return NULL;
} }
...@@ -220,7 +248,7 @@ ExecHashTableCreate(Hash *node, List *hashOperators) ...@@ -220,7 +248,7 @@ ExecHashTableCreate(Hash *node, List *hashOperators)
hashtable->nbatch_original = nbatch; hashtable->nbatch_original = nbatch;
hashtable->nbatch_outstart = nbatch; hashtable->nbatch_outstart = nbatch;
hashtable->growEnabled = true; hashtable->growEnabled = true;
hashtable->hashNonEmpty = false; hashtable->totalTuples = 0;
hashtable->innerBatchFile = NULL; hashtable->innerBatchFile = NULL;
hashtable->outerBatchFile = NULL; hashtable->outerBatchFile = NULL;
hashtable->spaceUsed = 0; hashtable->spaceUsed = 0;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.70 2005/03/31 02:02:52 neilc Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.71 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -123,13 +123,13 @@ ExecHashJoin(HashJoinState *node) ...@@ -123,13 +123,13 @@ ExecHashJoin(HashJoinState *node)
* execute the Hash node, to build the hash table * execute the Hash node, to build the hash table
*/ */
hashNode->hashtable = hashtable; hashNode->hashtable = hashtable;
(void) ExecProcNode((PlanState *) hashNode); (void) MultiExecProcNode((PlanState *) hashNode);
/* /*
* If the inner relation is completely empty, and we're not doing * If the inner relation is completely empty, and we're not doing
* an outer join, we can quit without scanning the outer relation. * an outer join, we can quit without scanning the outer relation.
*/ */
if (!hashtable->hashNonEmpty && node->js.jointype != JOIN_LEFT) if (hashtable->totalTuples == 0 && node->js.jointype != JOIN_LEFT)
{ {
ExecHashTableDestroy(hashtable); ExecHashTableDestroy(hashtable);
node->hj_HashTable = NULL; node->hj_HashTable = NULL;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.117 2005/03/16 21:38:09 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.118 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -105,6 +105,7 @@ extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ...@@ -105,6 +105,7 @@ extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
*/ */
extern PlanState *ExecInitNode(Plan *node, EState *estate); extern PlanState *ExecInitNode(Plan *node, EState *estate);
extern TupleTableSlot *ExecProcNode(PlanState *node); extern TupleTableSlot *ExecProcNode(PlanState *node);
extern Node *MultiExecProcNode(PlanState *node);
extern int ExecCountSlotsNode(Plan *node); extern int ExecCountSlotsNode(Plan *node);
extern void ExecEndNode(PlanState *node); extern void ExecEndNode(PlanState *node);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/hashjoin.h,v 1.35 2005/03/06 22:15:05 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/hashjoin.h,v 1.36 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -83,7 +83,7 @@ typedef struct HashJoinTableData ...@@ -83,7 +83,7 @@ typedef struct HashJoinTableData
bool growEnabled; /* flag to shut off nbatch increases */ bool growEnabled; /* flag to shut off nbatch increases */
bool hashNonEmpty; /* did inner plan produce any rows? */ double totalTuples; /* # tuples obtained from inner plan */
/* /*
* These arrays are allocated for the life of the hash join, but * These arrays are allocated for the life of the hash join, but
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 2001-2005, PostgreSQL Global Development Group * Copyright (c) 2001-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.10 2005/03/25 21:57:59 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/instrument.h,v 1.11 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -60,9 +60,9 @@ typedef struct Instrumentation ...@@ -60,9 +60,9 @@ typedef struct Instrumentation
/* Info about current plan cycle: */ /* Info about current plan cycle: */
bool running; /* TRUE if we've completed first tuple */ bool running; /* TRUE if we've completed first tuple */
instr_time starttime; /* Start time of current iteration of node */ instr_time starttime; /* Start time of current iteration of node */
instr_time counter; /* Accumulates runtime for this node */ instr_time counter; /* Accumulated runtime for this node */
double firsttuple; /* Time for first tuple of this cycle */ double firsttuple; /* Time for first tuple of this cycle */
double tuplecount; /* Tuples so far this cycle */ double tuplecount; /* Tuples emitted so far this cycle */
/* Accumulated statistics across all completed cycles: */ /* Accumulated statistics across all completed cycles: */
double startup; /* Total startup time (in seconds) */ double startup; /* Total startup time (in seconds) */
double total; /* Total total time (in seconds) */ double total; /* Total total time (in seconds) */
...@@ -73,6 +73,7 @@ typedef struct Instrumentation ...@@ -73,6 +73,7 @@ typedef struct Instrumentation
extern Instrumentation *InstrAlloc(int n); extern Instrumentation *InstrAlloc(int n);
extern void InstrStartNode(Instrumentation *instr); extern void InstrStartNode(Instrumentation *instr);
extern void InstrStopNode(Instrumentation *instr, bool returnedTuple); extern void InstrStopNode(Instrumentation *instr, bool returnedTuple);
extern void InstrStopNodeMulti(Instrumentation *instr, double nTuples);
extern void InstrEndLoop(Instrumentation *instr); extern void InstrEndLoop(Instrumentation *instr);
#endif /* INSTRUMENT_H */ #endif /* INSTRUMENT_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/executor/nodeHash.h,v 1.36 2005/03/06 22:15:05 tgl Exp $ * $PostgreSQL: pgsql/src/include/executor/nodeHash.h,v 1.37 2005/04/16 20:07:35 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
extern int ExecCountSlotsHash(Hash *node); extern int ExecCountSlotsHash(Hash *node);
extern HashState *ExecInitHash(Hash *node, EState *estate); extern HashState *ExecInitHash(Hash *node, EState *estate);
extern TupleTableSlot *ExecHash(HashState *node); extern TupleTableSlot *ExecHash(HashState *node);
extern Node *MultiExecHash(HashState *node);
extern void ExecEndHash(HashState *node); extern void ExecEndHash(HashState *node);
extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt); extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment