 src/backend/nodes/copyfuncs.c              |    8 ++
 src/backend/nodes/equalfuncs.c             |    8 ++
 src/backend/nodes/outfuncs.c               |    8 ++
 src/backend/nodes/readfuncs.c              |    8 ++
 src/backend/optimizer/path/costsize.c      |   17 ++++
 src/backend/optimizer/plan/createplan.c    |   13 +++-
 src/backend/optimizer/plan/planner.c       |  128 ++++++++++++++++++++++++++++
 src/backend/optimizer/util/clauses.c       |   24 ++++--
 src/include/nodes/primnodes.h              |    6 ++
 src/include/nodes/relation.h               |    1 +
 src/test/regress/expected/select_views.out |   91 +++++++++++++++++---
 src/test/regress/sql/select_views.sql      |   39 +++++++++
 12 files changed, 328 insertions(+), 23 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 63ff757..9fe6474 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1187,6 +1187,7 @@ _copyFuncExpr(FuncExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1223,6 +1224,7 @@ _copyOpExpr(OpExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1243,6 +1245,7 @@ _copyDistinctExpr(DistinctExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1263,6 +1266,7 @@ _copyNullIfExpr(NullIfExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1281,6 +1285,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1421,6 +1426,7 @@ _copyCoerceViaIO(CoerceViaIO *from)
 	COPY_SCALAR_FIELD(resultcollid);
 	COPY_SCALAR_FIELD(coerceformat);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1441,6 +1447,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from)
 	COPY_SCALAR_FIELD(isExplicit);
 	COPY_SCALAR_FIELD(coerceformat);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1573,6 +1580,7 @@ _copyRowCompareExpr(RowCompareExpr *from)
 	COPY_NODE_FIELD(inputcollids);
 	COPY_NODE_FIELD(largs);
 	COPY_NODE_FIELD(rargs);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cc61918..ac7a645 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -248,6 +248,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -285,6 +286,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -311,6 +313,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -337,6 +340,7 @@ _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -361,6 +365,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -479,6 +484,7 @@ _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b)
 		return false;
 
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -503,6 +509,7 @@ _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
 		return false;
 
 	COMPARE_LOCATION_FIELD(location);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
@@ -613,6 +620,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
 	COMPARE_NODE_FIELD(inputcollids);
 	COMPARE_NODE_FIELD(largs);
 	COMPARE_NODE_FIELD(rargs);
+	COMPARE_SCALAR_FIELD(depth);
 
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index fd204bd..c860eae 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1002,6 +1002,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1028,6 +1029,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1043,6 +1045,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1058,6 +1061,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1071,6 +1075,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1188,6 +1193,7 @@ _outCoerceViaIO(StringInfo str, CoerceViaIO *node)
 	WRITE_OID_FIELD(resultcollid);
 	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1203,6 +1209,7 @@ _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
 	WRITE_BOOL_FIELD(isExplicit);
 	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1295,6 +1302,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
 	WRITE_NODE_FIELD(inputcollids);
 	WRITE_NODE_FIELD(largs);
 	WRITE_NODE_FIELD(rargs);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 098f3c3..21ec851 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -541,6 +541,7 @@ _readFuncExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -588,6 +589,7 @@ _readOpExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -619,6 +621,7 @@ _readDistinctExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -650,6 +653,7 @@ _readNullIfExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -679,6 +683,7 @@ _readScalarArrayOpExpr(void)
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -794,6 +799,7 @@ _readCoerceViaIO(void)
 	READ_OID_FIELD(resultcollid);
 	READ_ENUM_FIELD(coerceformat, CoercionForm);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -814,6 +820,7 @@ _readArrayCoerceExpr(void)
 	READ_BOOL_FIELD(isExplicit);
 	READ_ENUM_FIELD(coerceformat, CoercionForm);
 	READ_LOCATION_FIELD(location);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
@@ -946,6 +953,7 @@ _readRowCompareExpr(void)
 	READ_NODE_FIELD(inputcollids);
 	READ_NODE_FIELD(largs);
 	READ_NODE_FIELD(rargs);
+	READ_INT_FIELD(depth);
 
 	READ_DONE();
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c853bd8..24ca5c8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2593,6 +2593,7 @@ cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
 	context.root = root;
 	context.total.startup = 0;
 	context.total.per_tuple = 0;
+	context.total.depth = 0;
 
 	/* We don't charge any cost for the implicit ANDing at top level ... */
 
@@ -2618,6 +2619,7 @@ cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
 	context.root = root;
 	context.total.startup = 0;
 	context.total.per_tuple = 0;
+	context.total.depth = 0;
 
 	cost_qual_eval_walker(qual, &context);
 
@@ -2647,6 +2649,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 			locContext.root = context->root;
 			locContext.total.startup = 0;
 			locContext.total.per_tuple = 0;
+			locContext.total.depth = 0;
 
 			/*
 			 * For an OR clause, recurse into the marked-up tree so that we
@@ -2671,6 +2674,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		}
 		context->total.startup += rinfo->eval_cost.startup;
 		context->total.per_tuple += rinfo->eval_cost.per_tuple;
+		if (rinfo->eval_cost.depth > context->total.depth)
+			context->total.depth = rinfo->eval_cost.depth;
 		/* do NOT recurse into children */
 		return false;
 	}
@@ -2694,6 +2699,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 	{
 		context->total.per_tuple +=
 			get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;
+		if (((FuncExpr *)node)->depth > context->total.depth)
+			context->total.depth = ((FuncExpr *)node)->depth;
 	}
 	else if (IsA(node, OpExpr) ||
 			 IsA(node, DistinctExpr) ||
@@ -2703,6 +2710,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		set_opfuncid((OpExpr *) node);
 		context->total.per_tuple +=
 			get_func_cost(((OpExpr *) node)->opfuncid) * cpu_operator_cost;
+		if (((OpExpr *)node)->depth > context->total.depth)
+			context->total.depth = ((OpExpr *)node)->depth;
 	}
 	else if (IsA(node, ScalarArrayOpExpr))
 	{
@@ -2716,6 +2725,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		set_sa_opfuncid(saop);
 		context->total.per_tuple += get_func_cost(saop->opfuncid) *
 			cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
+		if (saop->depth > context->total.depth)
+			context->total.depth = saop->depth;
 	}
 	else if (IsA(node, Aggref) ||
 			 IsA(node, WindowFunc))
@@ -2746,6 +2757,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		getTypeOutputInfo(exprType((Node *) iocoerce->arg),
 						  &iofunc, &typisvarlena);
 		context->total.per_tuple += get_func_cost(iofunc) * cpu_operator_cost;
+		if (iocoerce->depth > context->total.depth)
+			context->total.depth = iocoerce->depth;
 	}
 	else if (IsA(node, ArrayCoerceExpr))
 	{
@@ -2755,6 +2768,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 		if (OidIsValid(acoerce->elemfuncid))
 			context->total.per_tuple += get_func_cost(acoerce->elemfuncid) *
 				cpu_operator_cost * estimate_array_length(arraynode);
+		if (acoerce->depth > context->total.depth)
+			context->total.depth = acoerce->depth;
 	}
 	else if (IsA(node, RowCompareExpr))
 	{
@@ -2769,6 +2784,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 			context->total.per_tuple += get_func_cost(get_opcode(opid)) *
 				cpu_operator_cost;
 		}
+		if (rcexpr->depth > context->total.depth)
+			context->total.depth = rcexpr->depth;
 	}
 	else if (IsA(node, CurrentOfExpr))
 	{
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index a3a82ec..bb6cdd3 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2704,6 +2704,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 	{
 		Node	   *clause;
 		Cost		cost;
+		int			depth;
 	} QualItem;
 	int			nitems = list_length(clauses);
 	QualItem   *items;
@@ -2729,6 +2730,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 		cost_qual_eval_node(&qcost, clause, root);
 		items[i].clause = clause;
 		items[i].cost = qcost.per_tuple;
+		items[i].depth = qcost.depth;
 		i++;
 	}
 
@@ -2745,7 +2747,16 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 		/* insert newitem into the already-sorted subarray */
 		for (j = i; j > 0; j--)
 		{
-			if (newitem.cost >= items[j - 1].cost)
+			/*
+			 * Higher priority shall be given to the items originated from
+			 * deeper nest level. If same level, it shall be given to the
+			 * items with smaller estimated cost.
+			 * Such kind of consideration is needed to prevent leaky-view
+			 * problem.
+			 */
+			if (newitem.depth < items[j - 1].depth ||
+				(newitem.depth == items[j - 1].depth &&
+				 newitem.cost >= items[j - 1].cost))
 				break;
 			items[j] = items[j - 1];
 		}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7230fb4..71a1883 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -33,6 +33,7 @@
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
+#include "nodes/nodeFuncs.h"
 #ifdef OPTIMIZER_DEBUG
 #include "nodes/print.h"
 #endif
@@ -105,6 +106,7 @@ static void get_column_info_for_window(PlannerInfo *root, WindowClause *wc,
 						   int *ordNumCols,
 						   AttrNumber **ordColIdx,
 						   Oid **ordOperators);
+static void mark_qualifiers_depth(Query *query, int depth);
 
 
 /*****************************************************************************
@@ -150,6 +152,12 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 		cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;
 
 	/*
+	 * Mark qualifiers its original depth to prevent reversal of orders
+	 * on evaluation of WHERE clause during relation scanns.
+	 */
+	mark_qualifiers_depth(parse, 0);
+
+	/*
 	 * Set up global state for this planner invocation.  This data is needed
 	 * across all levels of sub-Query that might exist in the given command,
 	 * so we keep it in a separate struct that's linked to by each per-Query
@@ -3065,6 +3073,126 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
 	}
 }
 
+/*
+ * mark_qualifiers_depth
+ *
+ * It marks depth field of the each expression nodes that eventually
+ * invokes functions, to track the original nest-level. On the evaluation
+ * of qualifiers within WHERE or JOIN ... ON clauses during relation scans,
+ * these items shall be reordered according to the nest-level and estimated
+ * cost.
+ * The optimizer may pull-up simple sub-queries or join clause, and
+ * qualifiers to filter out tuples shall be mixed with ones in upper-
+ * level. Thus, we need to track the original nest-level of qualifiers
+ * to prevent reverse of order in evaluation, because some of qualifiers
+ * can have side-effects that allows to leak supplied argument to outside.
+ * It can be abused to break row-level security using a user defined function
+ * with very small estimated cost, so nest level of qualifiers originated
+ * from is used as a criteria, rather than estimated cost, to decide order
+ * to evaluate qualifiers.
+ */
+static bool
+mark_qualifiers_depth_walker(Node *node, void *context)
+{
+	int		depth = *((int *)(context));
+
+	if (node == NULL)
+		return false;
+	if (IsA(node, FuncExpr))
+	{
+		FuncExpr   *exp = (FuncExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, OpExpr))
+	{
+		OpExpr     *exp = (OpExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, DistinctExpr))
+	{
+		DistinctExpr   *exp = (DistinctExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, ScalarArrayOpExpr))
+	{
+		ScalarArrayOpExpr  *exp = (ScalarArrayOpExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, CoerceViaIO))
+	{
+		CoerceViaIO		   *exp = (CoerceViaIO *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, ArrayCoerceExpr))
+	{
+		ArrayCoerceExpr	   *exp = (ArrayCoerceExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, NullIfExpr))
+	{
+		NullIfExpr		   *exp = (NullIfExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	else if (IsA(node, RowCompareExpr))
+	{
+		RowCompareExpr	   *exp = (RowCompareExpr *)node;
+
+		exp->depth = depth;
+
+		return false;
+	}
+	return expression_tree_walker(node, mark_qualifiers_depth_walker, context);
+}
+
+static void
+mark_qualifiers_depth(Query *query, int depth)
+{
+	ListCell   *l;
+
+	foreach (l, query->rtable)
+	{
+		RangeTblEntry  *rte = lfirst(l);
+
+		/*
+		 * If and when sub-query is defined as a security-barrier,
+		 * any qualifiers of WHERE or JOIN ... ON clause must be
+		 * launched earlier than ones come from upper nest level,
+		 * even if the sub-query is enough simple to be pulled-up
+		 * later, because user can reference contents of tuples to
+		 * be invisible using functions with side-effect and much
+		 * smaller cost estimation.
+		 */
+		if (rte->rtekind == RTE_SUBQUERY)
+		{
+			if (rte->security_barrier)
+				mark_qualifiers_depth(rte->subquery, depth + 1);
+			else
+				mark_qualifiers_depth(rte->subquery, depth);
+		}
+	}
+	mark_qualifiers_depth_walker((Node *)query->jointree, &depth);
+}
 
 /*
  * expression_planner
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index efa986e..91c8429 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -110,6 +110,7 @@ static Expr *simplify_function(Expr *oldexpr, Oid funcid,
 				  Oid input_collid, List **args,
 				  bool has_named_args,
 				  bool allow_inline,
+				  int depth,
 				  eval_const_expressions_context *context);
 static List *reorder_function_arguments(List *args, Oid result_type,
 						   HeapTuple func_tuple,
@@ -122,7 +123,7 @@ static void recheck_cast_function_args(List *args, Oid result_type,
 						   HeapTuple func_tuple);
 static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
-				  HeapTuple func_tuple,
+				  HeapTuple func_tuple, int depth,
 				  eval_const_expressions_context *context);
 static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
 				Oid input_collid, List *args,
@@ -2193,7 +2194,7 @@ eval_const_expressions_mutator(Node *node,
 								   expr->funccollid,
 								   expr->inputcollid,
 								   &args,
-								   has_named_args, true, context);
+								   has_named_args, true, expr->depth, context);
 		if (simple)				/* successfully simplified it */
 			return (Node *) simple;
 
@@ -2212,6 +2213,7 @@ eval_const_expressions_mutator(Node *node,
 		newexpr->inputcollid = expr->inputcollid;
 		newexpr->args = args;
 		newexpr->location = expr->location;
+		newexpr->depth = expr->depth;
 		return (Node *) newexpr;
 	}
 	if (IsA(node, OpExpr))
@@ -2246,7 +2248,7 @@ eval_const_expressions_mutator(Node *node,
 								   expr->opcollid,
 								   expr->inputcollid,
 								   &args,
-								   false, true, context);
+								   false, true, expr->depth, context);
 		if (simple)				/* successfully simplified it */
 			return (Node *) simple;
 
@@ -2277,6 +2279,7 @@ eval_const_expressions_mutator(Node *node,
 		newexpr->inputcollid = expr->inputcollid;
 		newexpr->args = args;
 		newexpr->location = expr->location;
+		newexpr->depth = expr->depth;
 		return (Node *) newexpr;
 	}
 	if (IsA(node, DistinctExpr))
@@ -2344,7 +2347,7 @@ eval_const_expressions_mutator(Node *node,
 									   expr->opcollid,
 									   expr->inputcollid,
 									   &args,
-									   false, false, context);
+									   false, false, expr->depth, context);
 			if (simple)			/* successfully simplified it */
 			{
 				/*
@@ -2374,6 +2377,7 @@ eval_const_expressions_mutator(Node *node,
 		newexpr->inputcollid = expr->inputcollid;
 		newexpr->args = args;
 		newexpr->location = expr->location;
+		newexpr->depth = expr->depth;
 		return (Node *) newexpr;
 	}
 	if (IsA(node, BoolExpr))
@@ -2534,7 +2538,7 @@ eval_const_expressions_mutator(Node *node,
 								   InvalidOid,
 								   InvalidOid,
 								   &args,
-								   false, true, context);
+								   false, true, expr->depth, context);
 		if (simple)				/* successfully simplified output fn */
 		{
 			/*
@@ -2555,7 +2559,7 @@ eval_const_expressions_mutator(Node *node,
 									   expr->resultcollid,
 									   InvalidOid,
 									   &args,
-									   false, true, context);
+									   false, true, expr->depth, context);
 			if (simple)			/* successfully simplified input fn */
 				return (Node *) simple;
 		}
@@ -2571,6 +2575,7 @@ eval_const_expressions_mutator(Node *node,
 		newexpr->resultcollid = expr->resultcollid;
 		newexpr->coerceformat = expr->coerceformat;
 		newexpr->location = expr->location;
+		newexpr->depth = expr->depth;
 		return (Node *) newexpr;
 	}
 	if (IsA(node, ArrayCoerceExpr))
@@ -2595,6 +2600,7 @@ eval_const_expressions_mutator(Node *node,
 		newexpr->isExplicit = expr->isExplicit;
 		newexpr->coerceformat = expr->coerceformat;
 		newexpr->location = expr->location;
+		newexpr->depth = expr->depth;
 
 		/*
 		 * If constant argument and it's a binary-coercible or immutable
@@ -3411,6 +3417,7 @@ simplify_function(Expr *oldexpr, Oid funcid,
 				  Oid input_collid, List **args,
 				  bool has_named_args,
 				  bool allow_inline,
+				  int depth,
 				  eval_const_expressions_context *context)
 {
 	HeapTuple	func_tuple;
@@ -3441,7 +3448,7 @@ simplify_function(Expr *oldexpr, Oid funcid,
 
 	newexpr = evaluate_function(funcid, result_type, result_typmod,
 								result_collid, input_collid, *args,
-								func_tuple, context);
+								func_tuple, depth, context);
 
 	/*
 	 * Some functions calls can be simplified at plan time based on properties
@@ -3728,7 +3735,7 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
 static Expr *
 evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 				  Oid result_collid, Oid input_collid, List *args,
-				  HeapTuple func_tuple,
+				  HeapTuple func_tuple, int depth,
 				  eval_const_expressions_context *context)
 {
 	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
@@ -3814,6 +3821,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
 	newexpr->inputcollid = input_collid;
 	newexpr->args = args;
 	newexpr->location = -1;
+	newexpr->depth = depth;
 
 	return evaluate_expr((Expr *) newexpr, result_type, result_typmod,
 						 result_collid);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f1e20ef..b626386 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -335,6 +335,7 @@ typedef struct FuncExpr
 	Oid			inputcollid;	/* OID of collation that function should use */
 	List	   *args;			/* arguments to the function */
 	int			location;		/* token location, or -1 if unknown */
+	int			depth;			/* depth of clause in the original query */
 } FuncExpr;
 
 /*
@@ -380,6 +381,7 @@ typedef struct OpExpr
 	Oid			inputcollid;	/* OID of collation that operator should use */
 	List	   *args;			/* arguments to the operator (1 or 2) */
 	int			location;		/* token location, or -1 if unknown */
+	int			depth;			/* depth of clause in the original query */
 } OpExpr;
 
 /*
@@ -421,6 +423,7 @@ typedef struct ScalarArrayOpExpr
 	Oid			inputcollid;	/* OID of collation that operator should use */
 	List	   *args;			/* the scalar and array operands */
 	int			location;		/* token location, or -1 if unknown */
+	int			depth;			/* depth of clause in the original query */
 } ScalarArrayOpExpr;
 
 /*
@@ -685,6 +688,7 @@ typedef struct CoerceViaIO
 	Oid			resultcollid;	/* OID of collation, or InvalidOid if none */
 	CoercionForm coerceformat;	/* how to display this node */
 	int			location;		/* token location, or -1 if unknown */
+	int			depth;			/* depth of clause in the original query */
 } CoerceViaIO;
 
 /* ----------------
@@ -710,6 +714,7 @@ typedef struct ArrayCoerceExpr
 	bool		isExplicit;		/* conversion semantics flag to pass to func */
 	CoercionForm coerceformat;	/* how to display this node */
 	int			location;		/* token location, or -1 if unknown */
+	int			depth;			/* depth of clause in the original query */
 } ArrayCoerceExpr;
 
 /* ----------------
@@ -901,6 +906,7 @@ typedef struct RowCompareExpr
 	List	   *inputcollids;	/* OID list of collations for comparisons */
 	List	   *largs;			/* the left-hand input arguments */
 	List	   *rargs;			/* the right-hand input arguments */
+	int			depth;			/* depth of clause in the original query */
 } RowCompareExpr;
 
 /*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index f659269..250191b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -44,6 +44,7 @@ typedef struct QualCost
 {
 	Cost		startup;		/* one-time cost */
 	Cost		per_tuple;		/* per-evaluation cost */
+	int			depth;			/* depth of qual in the original query */
 } QualCost;
 
 /*
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 6cd317c..d1179d2 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -467,6 +467,20 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
  I- 580                             |       21
  I- 580                             |       22
  I- 580                             |       22
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        2
+ I- 580/I-680                  Ramp |        4
+ I- 580/I-680                  Ramp |        4
+ I- 580/I-680                  Ramp |        4
+ I- 580/I-680                  Ramp |        4
+ I- 580/I-680                  Ramp |        5
+ I- 580/I-680                  Ramp |        6
+ I- 580/I-680                  Ramp |        6
+ I- 580/I-680                  Ramp |        6
  I- 580                        Ramp |        2
  I- 580                        Ramp |        2
  I- 580                        Ramp |        2
@@ -717,20 +731,6 @@ SELECT name, #thepath FROM iexit ORDER BY 1, 2;
  I- 580                        Ramp |        8
  I- 580                        Ramp |        8
  I- 580                        Ramp |        8
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        2
- I- 580/I-680                  Ramp |        4
- I- 580/I-680                  Ramp |        4
- I- 580/I-680                  Ramp |        4
- I- 580/I-680                  Ramp |        4
- I- 580/I-680                  Ramp |        5
- I- 580/I-680                  Ramp |        6
- I- 580/I-680                  Ramp |        6
- I- 580/I-680                  Ramp |        6
  I- 680                             |        2
  I- 680                             |        2
  I- 680                             |        2
@@ -1247,3 +1247,66 @@ SELECT * FROM toyemp WHERE name = 'sharon';
  sharon |  25 | (15,12)  |     12000
 (1 row)
 
+--
+-- Test for leaky-view
+--
+CREATE USER alice;
+CREATE FUNCTION f_leak(text, text)
+	   RETURNS bool LANGUAGE 'plpgsql'
+	   COST 0.00000001
+	   AS 'begin raise notice ''% => %'', $1, $2; return true; end';
+CREATE TABLE credit_cards (
+    name   text,
+    number text,
+    expired    text
+);
+INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
+                                ('bob',   '5555-6666-7777-8888', 'Nov-2016'),
+                                ('eve',   '9801-2345-6789-0123', 'Jan-2018');
+CREATE VIEW your_credit_normal AS
+    SELECT * FROM credit_cards WHERE name = getpgusername();
+CREATE VIEW your_credit_secure WITH (security_barrier) AS
+    SELECT * FROM credit_cards WHERE name = getpgusername();
+GRANT SELECT ON your_credit_normal TO public;
+GRANT SELECT ON your_credit_secure TO public;
+-- run leaky view
+SET SESSION AUTHORIZATION alice;
+SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
+NOTICE:  1111-2222-3333-4444 => Aug-2012
+NOTICE:  5555-6666-7777-8888 => Nov-2016
+NOTICE:  9801-2345-6789-0123 => Jan-2018
+ name  |       number        | expired  
+-------+---------------------+----------
+ alice | 1111-2222-3333-4444 | Aug-2012
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Seq Scan on credit_cards
+   Filter: (f_leak(number, expired) AND (name = (getpgusername())::text))
+(2 rows)
+
+SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
+NOTICE:  1111-2222-3333-4444 => Aug-2012
+ name  |       number        | expired  
+-------+---------------------+----------
+ alice | 1111-2222-3333-4444 | Aug-2012
+(1 row)
+
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
+                                QUERY PLAN                                
+--------------------------------------------------------------------------
+ Seq Scan on credit_cards
+   Filter: ((name = (getpgusername())::text) AND f_leak(number, expired))
+(2 rows)
+
+\c -
+-- cleanups
+DROP ROLE IF EXISTS alice;
+DROP FUNCTION IF EXISTS f_leak(text);
+NOTICE:  function f_leak(text) does not exist, skipping
+DROP TABLE IF EXISTS credit_cards CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to view your_credit_normal
+drop cascades to view your_credit_secure
diff --git a/src/test/regress/sql/select_views.sql b/src/test/regress/sql/select_views.sql
index 14f1be8..dc49601 100644
--- a/src/test/regress/sql/select_views.sql
+++ b/src/test/regress/sql/select_views.sql
@@ -8,3 +8,42 @@ SELECT * FROM street;
 SELECT name, #thepath FROM iexit ORDER BY 1, 2;
 
 SELECT * FROM toyemp WHERE name = 'sharon';
+
+--
+-- Test for leaky-view
+--
+
+CREATE USER alice;
+CREATE FUNCTION f_leak(text, text)
+	   RETURNS bool LANGUAGE 'plpgsql'
+	   COST 0.00000001
+	   AS 'begin raise notice ''% => %'', $1, $2; return true; end';
+CREATE TABLE credit_cards (
+    name   text,
+    number text,
+    expired    text
+);
+INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
+                                ('bob',   '5555-6666-7777-8888', 'Nov-2016'),
+                                ('eve',   '9801-2345-6789-0123', 'Jan-2018');
+CREATE VIEW your_credit_normal AS
+    SELECT * FROM credit_cards WHERE name = getpgusername();
+CREATE VIEW your_credit_secure WITH (security_barrier) AS
+    SELECT * FROM credit_cards WHERE name = getpgusername();
+
+GRANT SELECT ON your_credit_normal TO public;
+GRANT SELECT ON your_credit_secure TO public;
+-- run leaky view
+SET SESSION AUTHORIZATION alice;
+
+SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
+
+SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
+EXPLAIN (COSTS OFF) SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
+
+\c -
+-- cleanups
+DROP ROLE IF EXISTS alice;
+DROP FUNCTION IF EXISTS f_leak(text);
+DROP TABLE IF EXISTS credit_cards CASCADE;
