On 12/09/2011 12:12, I wrote:
On 2011-09-10 19:50, Marti Raudsepp wrote:
I tried this patch and noticed something weird. This is probably not
intentional:
Indeed, it is not intentional. Will see how I can fix this.
The attached patch is the best I could come up with. I considered
showing "Rows Removed by Foo: (never executed)" and omitting the line
altogether, but I didn't particularly like either of those options. The
current patch simply displays "Rows Removed by Foo: 0".
I also added a comment the last patch was missing.
--
Marko Tiikkaja http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6408d16..1f42f46 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -64,9 +64,15 @@ static void show_qual(List *qual, const char *qlabel,
static void show_scan_qual(List *qual, const char *qlabel,
PlanState *planstate, List *ancestors,
ExplainState *es);
+static void show_instrumented_scan_qual(List *qual, const char *qlabel,
+ PlanState *planstate, double nremoved,
+ List *ancestors, ExplainState *es);
static void show_upper_qual(List *qual, const char *qlabel,
PlanState *planstate, List *ancestors,
ExplainState *es);
+static void show_instrumented_upper_qual(List *qual, const char *qlabel,
+ PlanState *planstate, double nremoved,
+ List *ancestors, ExplainState *es);
static void show_sort_keys(SortState *sortstate, List *ancestors,
ExplainState *es);
static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
@@ -1002,29 +1008,37 @@ ExplainNode(PlanState *planstate, List *ancestors,
"Index Cond", planstate,
ancestors, es);
show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
"Order By", planstate,
ancestors, es);
- show_scan_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_scan_qual(plan->qual, "Filter",
planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *)
plan)->indexqualorig,
"Index Cond", planstate,
ancestors, es);
break;
case T_BitmapHeapScan:
- show_scan_qual(((BitmapHeapScan *)
plan)->bitmapqualorig,
- "Recheck Cond", planstate,
ancestors, es);
+ show_instrumented_scan_qual(((BitmapHeapScan *)
plan)->bitmapqualorig,
+
"Recheck Cond", planstate,
+
((BitmapHeapScanState *) planstate)->bqonremoved,
+
ancestors, es);
/* FALL THRU */
case T_SeqScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
- show_scan_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_scan_qual(plan->qual, "Filter",
planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
break;
case T_FunctionScan:
if (es->verbose)
show_expression(((FunctionScan *)
plan)->funcexpr,
"Function
Call", planstate, ancestors,
es->verbose,
es);
- show_scan_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_scan_qual(plan->qual, "Filter",
planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
break;
case T_TidScan:
{
@@ -1037,35 +1051,47 @@ ExplainNode(PlanState *planstate, List *ancestors,
if (list_length(tidquals) > 1)
tidquals =
list_make1(make_orclause(tidquals));
show_scan_qual(tidquals, "TID Cond", planstate,
ancestors, es);
- show_scan_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_scan_qual(plan->qual,
"Filter", planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
}
break;
case T_ForeignScan:
- show_scan_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_scan_qual(plan->qual, "Filter",
planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
show_foreignscan_info((ForeignScanState *) planstate,
es);
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter",
planstate, ancestors, es);
- show_upper_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_upper_qual(plan->qual, "Filter",
planstate,
+
((JoinState *) planstate)->js_oqnremoved,
+
ancestors, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
"Merge Cond",
planstate, ancestors, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
"Join Filter",
planstate, ancestors, es);
- show_upper_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_upper_qual(plan->qual, "Filter",
planstate,
+
((JoinState *) planstate)->js_oqnremoved,
+
ancestors, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses,
"Hash Cond", planstate,
ancestors, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
"Join Filter",
planstate, ancestors, es);
- show_upper_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_upper_qual(plan->qual, "Filter",
planstate,
+
((JoinState *) planstate)->js_oqnremoved,
+
ancestors, es);
break;
case T_Agg:
case T_Group:
- show_upper_qual(plan->qual, "Filter", planstate,
ancestors, es);
+ show_instrumented_upper_qual(plan->qual, "Filter",
planstate,
+
((ScanState *) planstate)->ss_qualnremoved,
+
ancestors, es);
break;
case T_Sort:
show_sort_keys((SortState *) planstate, ancestors, es);
@@ -1356,6 +1382,31 @@ show_scan_qual(List *qual, const char *qlabel,
}
/*
+ * Show a qualifier expression and instrumentation information (if in EXPLAIN
+ * ANALYZE) for a scan plan node
+ */
+static void
+show_instrumented_scan_qual(List *qual, const char *qlabel,
+ PlanState *planstate,
double nremoved,
+ List *ancestors,
ExplainState *es)
+{
+ char buf[256];
+
+ show_scan_qual(qual, qlabel, planstate, ancestors, es);
+
+ if (!qual || !es->analyze)
+ return;
+
+ snprintf(buf, sizeof(buf), "Rows Removed by %s", qlabel);
+
+ if (planstate->instrument->nloops > 0)
+ ExplainPropertyFloat(buf, nremoved /
planstate->instrument->nloops, 0, es);
+ else
+ ExplainPropertyFloat(buf, 0.0, 0, es);
+}
+
+
+/*
* Show a qualifier expression for an upper-level plan node
*/
static void
@@ -1370,6 +1421,30 @@ show_upper_qual(List *qual, const char *qlabel,
}
/*
+ * Show a qualifier expression and instrumentation information (if in EXPLAIN
+ * ANALYZE) for an upper-level node
+ */
+static void
+show_instrumented_upper_qual(List *qual, const char *qlabel,
+ PlanState *planstate,
double nremoved,
+ List *ancestors,
ExplainState *es)
+{
+ char buf[256];
+
+ show_upper_qual(qual, qlabel, planstate, ancestors, es);
+
+ if (!qual || !es->analyze)
+ return;
+
+ snprintf(buf, sizeof(buf), "Rows Removed by %s", qlabel);
+
+ if (planstate->instrument->nloops > 0)
+ ExplainPropertyFloat(buf, nremoved /
planstate->instrument->nloops, 0, es);
+ else
+ ExplainPropertyFloat(buf, 0.0, 0, es);
+}
+
+/*
* Show the sort keys for a Sort node.
*/
static void
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index e900588..2de6b56 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -223,6 +223,7 @@ ExecScan(ScanState *node,
/*
* Tuple fails qual, so free per-tuple memory and try again.
*/
+ node->ss_qualnremoved += 1;
ResetExprContext(econtext);
}
}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 13d7723..0249c63 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1204,6 +1204,8 @@ agg_retrieve_direct(AggState *aggstate)
return result;
}
}
+ else
+ aggstate->ss.ss_qualnremoved += 1;
}
/* No more groups */
@@ -1354,6 +1356,8 @@ agg_retrieve_hash_table(AggState *aggstate)
return result;
}
}
+ else
+ aggstate->ss.ss_qualnremoved += 1;
}
/* No more groups */
@@ -1387,6 +1391,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
aggstate = makeNode(AggState);
aggstate->ss.ps.plan = (Plan *) node;
aggstate->ss.ps.state = estate;
+ aggstate->ss.ss_qualnremoved = 0;
aggstate->aggs = NIL;
aggstate->numaggs = 0;
diff --git a/src/backend/executor/nodeBitmapHeapscan.c
b/src/backend/executor/nodeBitmapHeapscan.c
index 8e50fb1..abb9ff4 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -278,6 +278,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
if (!ExecQual(node->bitmapqualorig, econtext, false))
{
/* Fails recheck, so drop it and loop back for
another */
+ node->bqonremoved += 1;
ExecClearTuple(slot);
continue;
}
@@ -542,7 +543,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState
*estate, int eflags)
scanstate = makeNode(BitmapHeapScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
+
+ scanstate->bqonremoved = 0;
scanstate->tbm = NULL;
scanstate->tbmiterator = NULL;
scanstate->tbmres = NULL;
diff --git a/src/backend/executor/nodeBitmapIndexscan.c
b/src/backend/executor/nodeBitmapIndexscan.c
index 9a56fd4..4dd1367 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -207,6 +207,7 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState
*estate, int eflags)
indexstate = makeNode(BitmapIndexScanState);
indexstate->ss.ps.plan = (Plan *) node;
indexstate->ss.ps.state = estate;
+ indexstate->ss.ss_qualnremoved = 0;
/* normally we don't make the result bitmap till runtime */
indexstate->biss_result = NULL;
diff --git a/src/backend/executor/nodeCtescan.c
b/src/backend/executor/nodeCtescan.c
index ec39f29..fdef1e8 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -191,6 +191,7 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
scanstate = makeNode(CteScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
scanstate->eflags = eflags;
scanstate->cte_table = NULL;
scanstate->eof_cte = false;
diff --git a/src/backend/executor/nodeForeignscan.c
b/src/backend/executor/nodeForeignscan.c
index 841ae69..6a25fd7 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -114,6 +114,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int
eflags)
scanstate = makeNode(ForeignScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeFunctionscan.c
b/src/backend/executor/nodeFunctionscan.c
index 5d5727e..894208c 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -133,6 +133,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate,
int eflags)
scanstate = makeNode(FunctionScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
scanstate->eflags = eflags;
/*
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index fa403e5..f167a39 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -118,6 +118,8 @@ ExecGroup(GroupState *node)
return result;
}
}
+ else
+ node->ss.ss_qualnremoved += 1;
}
/*
@@ -179,6 +181,8 @@ ExecGroup(GroupState *node)
return result;
}
}
+ else
+ node->ss.ss_qualnremoved += 1;
}
/* NOTREACHED */
@@ -206,6 +210,7 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
grpstate = makeNode(GroupState);
grpstate->ss.ps.plan = (Plan *) node;
grpstate->ss.ps.state = estate;
+ grpstate->ss.ss_qualnremoved = 0;
grpstate->grp_done = FALSE;
/*
diff --git a/src/backend/executor/nodeHashjoin.c
b/src/backend/executor/nodeHashjoin.c
index 3a66981..11aa4a6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -325,6 +325,8 @@ ExecHashJoin(HashJoinState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
}
break;
@@ -360,6 +362,8 @@ ExecHashJoin(HashJoinState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
}
break;
@@ -397,6 +401,8 @@ ExecHashJoin(HashJoinState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
break;
case HJ_NEED_NEW_BATCH:
@@ -442,6 +448,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
hjstate = makeNode(HashJoinState);
hjstate->js.ps.plan = (Plan *) node;
hjstate->js.ps.state = estate;
+ hjstate->js.js_oqnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeIndexscan.c
b/src/backend/executor/nodeIndexscan.c
index 955008e..cb705f4 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -468,6 +468,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int
eflags)
indexstate = makeNode(IndexScanState);
indexstate->ss.ps.plan = (Plan *) node;
indexstate->ss.ps.state = estate;
+ indexstate->ss.ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeMergejoin.c
b/src/backend/executor/nodeMergejoin.c
index e23dd6c..b3a1b10 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -893,6 +893,8 @@ ExecMergeJoin(MergeJoinState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
}
break;
@@ -1503,6 +1505,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int
eflags)
mergestate = makeNode(MergeJoinState);
mergestate->js.ps.plan = (Plan *) node;
mergestate->js.ps.state = estate;
+ mergestate->js.js_oqnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeNestloop.c
b/src/backend/executor/nodeNestloop.c
index e98bc0f..a4b9c92 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -214,6 +214,8 @@ ExecNestLoop(NestLoopState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
}
/*
@@ -270,6 +272,8 @@ ExecNestLoop(NestLoopState *node)
return result;
}
}
+ else
+ node->js.js_oqnremoved += 1;
}
/*
@@ -302,6 +306,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
nlstate = makeNode(NestLoopState);
nlstate->js.ps.plan = (Plan *) node;
nlstate->js.ps.state = estate;
+ nlstate->js.js_oqnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeSeqscan.c
b/src/backend/executor/nodeSeqscan.c
index 5b652c9..7a115c9 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -169,6 +169,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
scanstate = makeNode(SeqScanState);
scanstate->ps.plan = (Plan *) node;
scanstate->ps.state = estate;
+ scanstate->ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeSubqueryscan.c
b/src/backend/executor/nodeSubqueryscan.c
index 7e4d5de..26e0e70 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -109,6 +109,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate,
int eflags)
subquerystate = makeNode(SubqueryScanState);
subquerystate->ss.ps.plan = (Plan *) node;
subquerystate->ss.ps.state = estate;
+ subquerystate->ss.ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeTidscan.c
b/src/backend/executor/nodeTidscan.c
index 69f47ff..68d61b9 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -491,6 +491,7 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
tidstate = makeNode(TidScanState);
tidstate->ss.ps.plan = (Plan *) node;
tidstate->ss.ps.state = estate;
+ tidstate->ss.ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeValuesscan.c
b/src/backend/executor/nodeValuesscan.c
index d5260e4..5d474f6 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -205,6 +205,7 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int
eflags)
scanstate = makeNode(ValuesScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
/*
* Miscellaneous initialization
diff --git a/src/backend/executor/nodeWorktablescan.c
b/src/backend/executor/nodeWorktablescan.c
index bdebb6d..762605f 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -146,6 +146,7 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate,
int eflags)
scanstate = makeNode(WorkTableScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
+ scanstate->ss.ss_qualnremoved = 0;
scanstate->rustate = NULL; /* we'll set this later */
/*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b3eed7d..7139e8c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1170,6 +1170,7 @@ typedef struct ScanState
Relation ss_currentRelation;
HeapScanDesc ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
+ double ss_qualnremoved; /* number of rows removed by ps.qual */
} ScanState;
/*
@@ -1280,6 +1281,7 @@ typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is
NodeTag */
List *bitmapqualorig;
+ double bqonremoved; /* number of rows removed by
bitmapqualorig */
TIDBitmap *tbm;
TBMIterator *tbmiterator;
TBMIterateResult *tbmres;
@@ -1438,6 +1440,7 @@ typedef struct JoinState
PlanState ps;
JoinType jointype;
List *joinqual; /* JOIN quals (in addition to ps.qual)
*/
+ double js_oqnremoved; /* number of rows removed by otherqual
(ps.qual) */
} JoinState;
/* ----------------
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers