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;
 
 /* ----------------
