 src/backend/commands/explain.c | 107 +++++++++++++++++++++++++++--------------
 1 file changed, 70 insertions(+), 37 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 91bea51..4ea4920 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -57,7 +57,7 @@ static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
 static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used);
 static void ExplainNode(PlanState *planstate, List *ancestors,
 			const char *relationship, const char *plan_name,
-			ExplainState *es);
+			bool security_barrier, ExplainState *es);
 static void show_plan_tlist(PlanState *planstate, List *ancestors,
 				ExplainState *es);
 static void show_expression(Node *node, const char *qlabel,
@@ -92,9 +92,12 @@ static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
 static void show_modifytable_info(ModifyTableState *mtstate, ExplainState *es);
 static void ExplainMemberNodes(List *plans, PlanState **planstates,
-				   List *ancestors, ExplainState *es);
+				List *ancestors, bool security_barrier, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
-				const char *relationship, ExplainState *es);
+				const char *relationship, bool security_barrier,
+				ExplainState *es);
+static void ExplainSubQuery(SubqueryScanState *qstate, List *ancestors,
+				bool security_barrier, ExplainState *es);
 static void ExplainProperty(const char *qlabel, const char *value,
 				bool numeric, ExplainState *es);
 static void ExplainOpenGroup(const char *objtype, const char *labelname,
@@ -555,7 +558,7 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
 	es->rtable = queryDesc->plannedstmt->rtable;
 	ExplainPreScanNode(queryDesc->planstate, &rels_used);
 	es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
-	ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
+	ExplainNode(queryDesc->planstate, NIL, NULL, NULL, false, es);
 }
 
 /*
@@ -794,6 +797,10 @@ ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used)
  * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
  * optional name to be attached to the node.
  *
+ * security_barrier shows whether this plan come from a view with security
+ * barrier attribute, or not. If true, it restrain a part of output to
+ * avoid information leakage to be invisible.
+ *
  * In text format, es->indent is controlled in this function since we only
  * want it to change at plan-node boundaries.  In non-text formats, es->indent
  * corresponds to the nesting depth of logical output groups, and therefore
@@ -802,7 +809,7 @@ ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used)
 static void
 ExplainNode(PlanState *planstate, List *ancestors,
 			const char *relationship, const char *plan_name,
-			ExplainState *es)
+			bool security_barrier, ExplainState *es)
 {
 	Plan	   *plan = planstate->plan;
 	const char *pname;			/* node type name for text output */
@@ -1210,26 +1217,26 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_IndexScan:
 			show_scan_qual(((IndexScan *) plan)->indexqualorig,
 						   "Index Cond", planstate, ancestors, es);
-			if (((IndexScan *) plan)->indexqualorig)
+			if (((IndexScan *) plan)->indexqualorig && !security_barrier)
 				show_instrumentation_count("Rows Removed by Index Recheck", 2,
 										   planstate, es);
 			show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
 						   "Order By", planstate, ancestors, es);
 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
 		case T_IndexOnlyScan:
 			show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
 						   "Index Cond", planstate, ancestors, es);
-			if (((IndexOnlyScan *) plan)->indexqual)
+			if (((IndexOnlyScan *) plan)->indexqual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Index Recheck", 2,
 										   planstate, es);
 			show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
 						   "Order By", planstate, ancestors, es);
 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			if (es->analyze)
@@ -1243,7 +1250,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_BitmapHeapScan:
 			show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
 						   "Recheck Cond", planstate, ancestors, es);
-			if (((BitmapHeapScan *) plan)->bitmapqualorig)
+			if (((BitmapHeapScan *) plan)->bitmapqualorig && !security_barrier)
 				show_instrumentation_count("Rows Removed by Index Recheck", 2,
 										   planstate, es);
 			/* FALL THRU */
@@ -1253,7 +1260,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_WorkTableScan:
 		case T_SubqueryScan:
 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
@@ -1263,7 +1270,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 								"Function Call", planstate, ancestors,
 								es->verbose, es);
 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
@@ -1279,14 +1286,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 					tidquals = list_make1(make_orclause(tidquals));
 				show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
 				show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-				if (plan->qual)
+				if (plan->qual && !security_barrier)
 					show_instrumentation_count("Rows Removed by Filter", 1,
 											   planstate, es);
 			}
 			break;
 		case T_ForeignScan:
 			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			show_foreignscan_info((ForeignScanState *) planstate, es);
@@ -1294,11 +1301,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
-			if (((NestLoop *) plan)->join.joinqual)
+			if (((NestLoop *) plan)->join.joinqual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Join Filter", 1,
 										   planstate, es);
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 2,
 										   planstate, es);
 			break;
@@ -1307,11 +1314,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
 							"Merge Cond", planstate, ancestors, es);
 			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
-			if (((MergeJoin *) plan)->join.joinqual)
+			if (((MergeJoin *) plan)->join.joinqual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Join Filter", 1,
 										   planstate, es);
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 2,
 										   planstate, es);
 			break;
@@ -1320,18 +1327,18 @@ ExplainNode(PlanState *planstate, List *ancestors,
 							"Hash Cond", planstate, ancestors, es);
 			show_upper_qual(((HashJoin *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
-			if (((HashJoin *) plan)->join.joinqual)
+			if (((HashJoin *) plan)->join.joinqual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Join Filter", 1,
 										   planstate, es);
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 2,
 										   planstate, es);
 			break;
 		case T_Agg:
 		case T_Group:
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
@@ -1347,7 +1354,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
 							"One-Time Filter", planstate, ancestors, es);
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
-			if (plan->qual)
+			if (plan->qual && !security_barrier)
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
@@ -1487,17 +1494,18 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
 	/* initPlan-s */
 	if (planstate->initPlan)
-		ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
+		ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan",
+						security_barrier, es);
 
 	/* lefttree */
 	if (outerPlanState(planstate))
 		ExplainNode(outerPlanState(planstate), ancestors,
-					"Outer", NULL, es);
+					"Outer", NULL, security_barrier, es);
 
 	/* righttree */
 	if (innerPlanState(planstate))
 		ExplainNode(innerPlanState(planstate), ancestors,
-					"Inner", NULL, es);
+					"Inner", NULL, security_barrier, es);
 
 	/* special child plans */
 	switch (nodeTag(plan))
@@ -1505,31 +1513,31 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ModifyTable:
 			ExplainMemberNodes(((ModifyTable *) plan)->plans,
 							   ((ModifyTableState *) planstate)->mt_plans,
-							   ancestors, es);
+							   ancestors, security_barrier, es);
 			break;
 		case T_Append:
 			ExplainMemberNodes(((Append *) plan)->appendplans,
 							   ((AppendState *) planstate)->appendplans,
-							   ancestors, es);
+							   ancestors, security_barrier, es);
 			break;
 		case T_MergeAppend:
 			ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
 							   ((MergeAppendState *) planstate)->mergeplans,
-							   ancestors, es);
+							   ancestors, security_barrier, es);
 			break;
 		case T_BitmapAnd:
 			ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
 							   ((BitmapAndState *) planstate)->bitmapplans,
-							   ancestors, es);
+							   ancestors, security_barrier, es);
 			break;
 		case T_BitmapOr:
 			ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
 							   ((BitmapOrState *) planstate)->bitmapplans,
-							   ancestors, es);
+							   ancestors, security_barrier, es);
 			break;
 		case T_SubqueryScan:
-			ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
-						"Subquery", NULL, es);
+			ExplainSubQuery((SubqueryScanState *) planstate,
+							ancestors, security_barrier, es);
 			break;
 		default:
 			break;
@@ -1537,7 +1545,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
 	/* subPlan-s */
 	if (planstate->subPlan)
-		ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
+		ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan",
+						security_barrier, es);
 
 	/* end of child plans */
 	if (haschildren)
@@ -2090,14 +2099,14 @@ show_modifytable_info(ModifyTableState *mtstate, ExplainState *es)
  */
 static void
 ExplainMemberNodes(List *plans, PlanState **planstates,
-				   List *ancestors, ExplainState *es)
+				   List *ancestors, bool security_barrier, ExplainState *es)
 {
 	int			nplans = list_length(plans);
 	int			j;
 
 	for (j = 0; j < nplans; j++)
 		ExplainNode(planstates[j], ancestors,
-					"Member", NULL, es);
+					"Member", NULL, security_barrier, es);
 }
 
 /*
@@ -2108,7 +2117,8 @@ ExplainMemberNodes(List *plans, PlanState **planstates,
  */
 static void
 ExplainSubPlans(List *plans, List *ancestors,
-				const char *relationship, ExplainState *es)
+				const char *relationship, bool security_barrier,
+				ExplainState *es)
 {
 	ListCell   *lst;
 
@@ -2118,11 +2128,34 @@ ExplainSubPlans(List *plans, List *ancestors,
 		SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
 
 		ExplainNode(sps->planstate, ancestors,
-					relationship, sp->plan_name, es);
+					relationship, sp->plan_name, security_barrier, es);
 	}
 }
 
 /*
+ * Explain a child plan node of a particular sub-query
+ */
+static void
+ExplainSubQuery(SubqueryScanState *qstate, List *ancestors,
+				bool security_barrier, ExplainState *es)
+{
+	bool	security_barrier_child;
+
+	if (!security_barrier)
+	{
+		SubqueryScan   *qscan = (SubqueryScan *)qstate->ss.ps.plan;
+		RangeTblEntry  *rte = rt_fetch(qscan->scan.scanrelid, es->rtable);
+
+		security_barrier_child = rte->security_barrier;
+	}
+	else
+		security_barrier_child = true;
+
+	ExplainNode(qstate->subplan, ancestors, "Subquery", NULL,
+				security_barrier_child, es);
+}
+
+/*
  * Explain a property, such as sort keys or targets, that takes the form of
  * a list of unlabeled items.  "data" is a list of C strings.
  */
