Create a new 'MultiExecProcNode' call API for plan nodes that don't
authorTom Lane
Sat, 16 Apr 2005 20:07:35 +0000 (20:07 +0000)
committerTom Lane
Sat, 16 Apr 2005 20:07:35 +0000 (20:07 +0000)
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.

src/backend/commands/explain.c
src/backend/executor/execProcnode.c
src/backend/executor/instrument.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/include/executor/executor.h
src/include/executor/hashjoin.h
src/include/executor/instrument.h
src/include/executor/nodeHash.h

index 02ceedd39598f1de2a6ac8aa07e2793cb786d4c4..4b2abb9e7b4a468fccb51b5c1ddde141b6680f40 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * 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
 
 static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
                TupOutputState *tstate);
-static double elapsed_time(instr_time * starttime);
+static double elapsed_time(instr_time *starttime);
 static void explain_outNode(StringInfo str,
                Plan *plan, PlanState *planstate,
                Plan *outer_plan,
@@ -296,7 +296,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
        {
            int     nt;
 
-           if (!rInfo->ri_TrigDesc)
+           if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
                continue;
            for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
            {
@@ -366,7 +366,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
 
 /* Compute elapsed time in seconds since given timestamp */
 static double
-elapsed_time(instr_time * starttime)
+elapsed_time(instr_time *starttime)
 {
    instr_time endtime;
 
@@ -663,7 +663,8 @@ explain_outNode(StringInfo str,
         * We have to forcibly clean up the instrumentation state because
         * 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)
        {
index ff8caf16f01233030b4f5e6ba2660538ffbc1099..555668e77991af4b0477232e71fa19b2a05f4239 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * 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)
    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
  *
index 52d9de4f4ac2898f3483c6eeca766d381138d715..c5b4a252d612e664498bac83a3c39e05ca0fdb04 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2001-2005, PostgreSQL Global Development Group
  *
  * 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)
 void
 InstrStartNode(Instrumentation *instr)
 {
-   if (!instr)
-       return;
-
-   if (!INSTR_TIME_IS_ZERO(instr->starttime))
-       elog(DEBUG2, "InstrStartNode called twice in a row");
-   else
+   if (INSTR_TIME_IS_ZERO(instr->starttime))
        INSTR_TIME_SET_CURRENT(instr->starttime);
+   else
+       elog(DEBUG2, "InstrStartNode called twice in a row");
 }
 
 /* Exit from a plan node */
@@ -48,12 +45,13 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple)
 {
    instr_time endtime;
 
-   if (!instr)
-       return;
+   /* count the returned tuples */
+   if (returnedTuple)
+       instr->tuplecount += 1;
 
    if (INSTR_TIME_IS_ZERO(instr->starttime))
    {
-       elog(DEBUG2, "InstrStopNode without start");
+       elog(DEBUG2, "InstrStopNode called without start");
        return;
    }
 
@@ -86,9 +84,17 @@ InstrStopNode(Instrumentation *instr, bool returnedTuple)
        instr->running = true;
        instr->firsttuple = INSTR_TIME_GET_DOUBLE(instr->counter);
    }
+}
 
-   if (returnedTuple)
-       instr->tuplecount += 1;
+/* As above, but count multiple tuples returned at once */
+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 */
@@ -97,14 +103,14 @@ InstrEndLoop(Instrumentation *instr)
 {
    double      totaltime;
 
-   if (!instr)
-       return;
-
    /* Skip if nothing has happened, or already shut down */
    if (!instr->running)
        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);
 
    instr->startup += instr->firsttuple;
index 97e6738bd3203fed7b60af474abb689a8f420545..c304d930a287a768d43e825fe8441abcfcf4f7e5 100644 (file)
@@ -8,13 +8,13 @@
  *
  *
  * 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
- *     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
  *     ExecEndHash     - shutdown node and subnodes
  */
@@ -22,6 +22,7 @@
 
 #include "executor/execdebug.h"
 #include "executor/hashjoin.h"
+#include "executor/instrument.h"
 #include "executor/nodeHash.h"
 #include "executor/nodeHashjoin.h"
 #include "miscadmin.h"
@@ -36,12 +37,25 @@ static void ExecHashIncreaseNumBatches(HashJoinTable hashtable);
 /* ----------------------------------------------------------------
  *     ExecHash
  *
- *     build hash table for hashjoin, doing partitioning if more
- *     than one batch is required.
+ *     stub for pro forma compliance
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
 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;
    List       *hashkeys;
@@ -50,6 +64,10 @@ ExecHash(HashState *node)
    ExprContext *econtext;
    uint32      hashvalue;
 
+   /* must provide our own instrumentation support */
+   if (node->ps.instrument)
+       InstrStartNode(node->ps.instrument);
+
    /*
     * get state info from node
     */
@@ -70,14 +88,24 @@ ExecHash(HashState *node)
        slot = ExecProcNode(outerNode);
        if (TupIsNull(slot))
            break;
-       hashtable->hashNonEmpty = true;
+       hashtable->totalTuples += 1;
        /* We have to compute the hash value */
        econtext->ecxt_innertuple = slot;
        hashvalue = ExecHashGetHashValue(hashtable, econtext, hashkeys);
        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;
 }
 
@@ -220,7 +248,7 @@ ExecHashTableCreate(Hash *node, List *hashOperators)
    hashtable->nbatch_original = nbatch;
    hashtable->nbatch_outstart = nbatch;
    hashtable->growEnabled = true;
-   hashtable->hashNonEmpty = false;
+   hashtable->totalTuples = 0;
    hashtable->innerBatchFile = NULL;
    hashtable->outerBatchFile = NULL;
    hashtable->spaceUsed = 0;
index 4811b7068ebcaff9481a94eadf5d39096dd1d68f..38e48cd6dcec878cca5fdc9e78bd4469c47476c1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
         * execute the Hash node, to build the hash table
         */
        hashNode->hashtable = hashtable;
-       (void) ExecProcNode((PlanState *) hashNode);
+       (void) MultiExecProcNode((PlanState *) hashNode);
 
        /*
         * If the inner relation is completely empty, and we're not doing
         * 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);
            node->hj_HashTable = NULL;
index 0d3e18ce0ac41291c0458fed5028649a35146807..2e42894788eebdeb2f6bb05311d0659cf9904229 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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,
  */
 extern PlanState *ExecInitNode(Plan *node, EState *estate);
 extern TupleTableSlot *ExecProcNode(PlanState *node);
+extern Node *MultiExecProcNode(PlanState *node);
 extern int ExecCountSlotsNode(Plan *node);
 extern void ExecEndNode(PlanState *node);
 
index c0f75922e12e0da55b7030819bc847c2f9c4be73..f5200831d7e713e588784b52dfe08ac8a2261ca8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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
 
    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
index 0540fd0da71cab07596e9575968bc7625c5426ff..47899fbcc2613b768a0f500fdd980b1f81fc4e70 100644 (file)
@@ -6,7 +6,7 @@
  *
  * 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
    /* Info about current plan cycle: */
    bool        running;        /* TRUE if we've completed first tuple */
    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      tuplecount;     /* Tuples so far this cycle */
+   double      tuplecount;     /* Tuples emitted so far this cycle */
    /* Accumulated statistics across all completed cycles: */
    double      startup;        /* Total startup time (in seconds) */
    double      total;          /* Total total time (in seconds) */
@@ -73,6 +73,7 @@ typedef struct Instrumentation
 extern Instrumentation *InstrAlloc(int n);
 extern void InstrStartNode(Instrumentation *instr);
 extern void InstrStopNode(Instrumentation *instr, bool returnedTuple);
+extern void InstrStopNodeMulti(Instrumentation *instr, double nTuples);
 extern void InstrEndLoop(Instrumentation *instr);
 
 #endif   /* INSTRUMENT_H */
index 06d73c060ec76ce1e11b6da36eb311b31afe5b25..678b2bd76229ba75f043f65069a7f6f6244a6df7 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * 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 @@
 extern int ExecCountSlotsHash(Hash *node);
 extern HashState *ExecInitHash(Hash *node, EState *estate);
 extern TupleTableSlot *ExecHash(HashState *node);
+extern Node *MultiExecHash(HashState *node);
 extern void ExecEndHash(HashState *node);
 extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt);