 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      |    3 +-
 src/backend/optimizer/prep/prepjointree.c |  171 +++++++++++++++++++++++++----
 src/backend/optimizer/util/clauses.c      |   24 +++--
 src/include/nodes/primnodes.h             |    6 +
 src/include/nodes/relation.h              |    1 +
 src/include/optimizer/prep.h              |    3 +-
 12 files changed, 234 insertions(+), 36 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 661a516..4e61789 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1183,6 +1183,7 @@ _copyFuncExpr(FuncExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1219,6 +1220,7 @@ _copyOpExpr(OpExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1239,6 +1241,7 @@ _copyDistinctExpr(DistinctExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1259,6 +1262,7 @@ _copyNullIfExpr(NullIfExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1277,6 +1281,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1417,6 +1422,7 @@ _copyCoerceViaIO(CoerceViaIO *from)
 	COPY_SCALAR_FIELD(resultcollid);
 	COPY_SCALAR_FIELD(coerceformat);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1437,6 +1443,7 @@ _copyArrayCoerceExpr(ArrayCoerceExpr *from)
 	COPY_SCALAR_FIELD(isExplicit);
 	COPY_SCALAR_FIELD(coerceformat);
 	COPY_LOCATION_FIELD(location);
+	COPY_SCALAR_FIELD(depth);
 
 	return newnode;
 }
@@ -1569,6 +1576,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 4052a9a..abef8a7 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 0d0ce3c..16a0239 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -997,6 +997,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1023,6 +1024,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1038,6 +1040,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1053,6 +1056,7 @@ _outNullIfExpr(StringInfo str, NullIfExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1066,6 +1070,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
 	WRITE_LOCATION_FIELD(location);
+	WRITE_INT_FIELD(depth);
 }
 
 static void
@@ -1183,6 +1188,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
@@ -1198,6 +1204,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
@@ -1290,6 +1297,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 29a0e8f..c4f934d 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 7812a86..0a758dc 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 0dedeba..8460c4b 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2702,6 +2702,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 	{
 		Node	   *clause;
 		Cost		cost;
+		int			depth;
 	} QualItem;
 	int			nitems = list_length(clauses);
 	QualItem   *items;
@@ -2727,6 +2728,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++;
 	}
 
@@ -2743,7 +2745,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 b498274..5f17177 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -334,7 +334,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
 	 * query.
 	 */
 	parse->jointree = (FromExpr *)
-		pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL);
+		pull_up_subqueries(root, (Node *) parse->jointree, NULL, NULL, 0);
 
 	/*
 	 * If this is a simple UNION ALL query, flatten it into an appendrel. We
@@ -3155,7 +3155,6 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
 	}
 }
 
-
 /*
  * expression_planner
  *		Perform planner's transformations on a standalone expression.
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index aeaae8c..4bd9956 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -23,6 +23,7 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_class.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -34,6 +35,7 @@
 #include "parser/parse_relation.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/syscache.h"
 
 
 typedef struct pullup_replace_vars_context
@@ -62,12 +64,13 @@ static Node *pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
 static Node *pull_up_simple_subquery(PlannerInfo *root, Node *jtnode,
 						RangeTblEntry *rte,
 						JoinExpr *lowest_outer_join,
-						AppendRelInfo *containing_appendrel);
+						AppendRelInfo *containing_appendrel,
+						int qubquery_depth);
 static Node *pull_up_simple_union_all(PlannerInfo *root, Node *jtnode,
-						 RangeTblEntry *rte);
+						 RangeTblEntry *rte, int subquery_depth);
 static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root,
 						   int parentRTindex, Query *setOpQuery,
-						   int childRToffset);
+						   int childRToffset, int subquery_depth);
 static void make_setop_translation_list(Query *query, Index newvarno,
 							List **translated_vars);
 static bool is_simple_subquery(Query *subquery);
@@ -91,6 +94,7 @@ static void reduce_outer_joins_pass2(Node *jtnode,
 						 List *forced_null_vars);
 static void substitute_multiple_relids(Node *node,
 						   int varno, Relids subrelids);
+static void mark_qualifiers_depth(Node *node, int depth);
 static void fix_append_rel_relids(List *append_rel_list, int varno,
 					  Relids subrelids);
 static Node *find_jointree_node_for_rel(Node *jtnode, int relid);
@@ -525,7 +529,8 @@ inline_set_returning_functions(PlannerInfo *root)
 Node *
 pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 				   JoinExpr *lowest_outer_join,
-				   AppendRelInfo *containing_appendrel)
+				   AppendRelInfo *containing_appendrel,
+				   int subquery_depth)
 {
 	if (jtnode == NULL)
 		return NULL;
@@ -547,7 +552,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 			 is_safe_append_member(rte->subquery)))
 			return pull_up_simple_subquery(root, jtnode, rte,
 										   lowest_outer_join,
-										   containing_appendrel);
+										   containing_appendrel,
+										   subquery_depth);
 
 		/*
 		 * Alternatively, is it a simple UNION ALL subquery?  If so, flatten
@@ -560,7 +566,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 		 */
 		if (rte->rtekind == RTE_SUBQUERY &&
 			is_simple_union_all(rte->subquery))
-			return pull_up_simple_union_all(root, jtnode, rte);
+			return pull_up_simple_union_all(root, jtnode, rte,
+											subquery_depth);
 
 		/* Otherwise, do nothing at this node. */
 	}
@@ -572,7 +579,16 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 		Assert(containing_appendrel == NULL);
 		foreach(l, f->fromlist)
 			lfirst(l) = pull_up_subqueries(root, lfirst(l),
-										   lowest_outer_join, NULL);
+										   lowest_outer_join, NULL,
+										   subquery_depth);
+		/*
+		 * Mark the sub-query depth of qualifiers to determine the original
+		 * level of them, if necessary. Expr->depth is initialized to zero,
+		 * so we don't need to walk on the expression tree, if security view
+		 * was not used.
+		 */
+		if (subquery_depth > 0)
+			mark_qualifiers_depth(f->quals, subquery_depth);
 	}
 	else if (IsA(jtnode, JoinExpr))
 	{
@@ -584,35 +600,45 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 		{
 			case JOIN_INNER:
 				j->larg = pull_up_subqueries(root, j->larg,
-											 lowest_outer_join, NULL);
+											 lowest_outer_join, NULL,
+											 subquery_depth);
 				j->rarg = pull_up_subqueries(root, j->rarg,
-											 lowest_outer_join, NULL);
+											 lowest_outer_join, NULL,
+											 subquery_depth);
 				break;
 			case JOIN_LEFT:
 			case JOIN_SEMI:
 			case JOIN_ANTI:
 				j->larg = pull_up_subqueries(root, j->larg,
-											 lowest_outer_join, NULL);
+											 lowest_outer_join, NULL,
+											 subquery_depth);
 				j->rarg = pull_up_subqueries(root, j->rarg,
-											 j, NULL);
+											 j, NULL,
+											 subquery_depth);
 				break;
 			case JOIN_FULL:
 				j->larg = pull_up_subqueries(root, j->larg,
-											 j, NULL);
+											 j, NULL,
+											 subquery_depth);
 				j->rarg = pull_up_subqueries(root, j->rarg,
-											 j, NULL);
+											 j, NULL,
+											 subquery_depth);
 				break;
 			case JOIN_RIGHT:
 				j->larg = pull_up_subqueries(root, j->larg,
-											 j, NULL);
+											 j, NULL,
+											 subquery_depth);
 				j->rarg = pull_up_subqueries(root, j->rarg,
-											 lowest_outer_join, NULL);
+											 lowest_outer_join, NULL,
+											 subquery_depth);
 				break;
 			default:
 				elog(ERROR, "unrecognized join type: %d",
 					 (int) j->jointype);
 				break;
 		}
+		if (subquery_depth > 0)
+			mark_qualifiers_depth(j->quals, subquery_depth);
 	}
 	else
 		elog(ERROR, "unrecognized node type: %d",
@@ -635,7 +661,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 static Node *
 pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 						JoinExpr *lowest_outer_join,
-						AppendRelInfo *containing_appendrel)
+						AppendRelInfo *containing_appendrel,
+						int subquery_depth)
 {
 	Query	   *parse = root->parse;
 	int			varno = ((RangeTblRef *) jtnode)->rtindex;
@@ -679,6 +706,31 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	Assert(subquery->cteList == NIL);
 
 	/*
+	 * Increment subquery_depth, if the supplied sub-query come from
+	 * a view defined with CREATE SECURITY VIEW. This depth value shall
+	 * be checked to determine the order to launch qualifiers during
+	 * table scans. It enables to prevent unexpected information leaks
+	 * due to the interaction between query optimization and functions
+	 * with side-effects.
+	 */
+	if (OidIsValid(rte->relid))
+	{
+		HeapTuple		tup;
+		Form_pg_class	class_form;
+
+		tup = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid));
+        if (!HeapTupleIsValid(tup))
+            elog(ERROR, "cache lookup failed for relation %u", rte->relid);
+
+		class_form = (Form_pg_class) GETSTRUCT(tup);
+		Assert(class_form->relkind == RELKIND_VIEW);
+		if (class_form->relissecbarrier)
+			subquery_depth += 1;
+
+		ReleaseSysCache(tup);
+	}
+
+	/*
 	 * Pull up any SubLinks within the subquery's quals, so that we don't
 	 * leave unoptimized SubLinks behind.
 	 */
@@ -701,7 +753,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
 	 * handling an appendrel member.
 	 */
 	subquery->jointree = (FromExpr *)
-		pull_up_subqueries(subroot, (Node *) subquery->jointree, NULL, NULL);
+		pull_up_subqueries(subroot, (Node *) subquery->jointree,
+						   NULL, NULL, subquery_depth);
 
 	/*
 	 * Now we must recheck whether the subquery is still simple enough to pull
@@ -903,7 +956,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
  * jtnode, since we don't actually need to change the query jointree.
  */
 static Node *
-pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
+pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
+						 int subquery_depth)
 {
 	int			varno = ((RangeTblRef *) jtnode)->rtindex;
 	Query	   *subquery = rte->subquery;
@@ -930,7 +984,7 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 	 */
 	Assert(subquery->setOperations);
 	pull_up_union_leaf_queries(subquery->setOperations, root, varno, subquery,
-							   rtoffset);
+							   rtoffset, subquery_depth);
 
 	/*
 	 * Mark the parent as an append relation.
@@ -960,7 +1014,8 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
  */
 static void
 pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
-						   Query *setOpQuery, int childRToffset)
+						   Query *setOpQuery, int childRToffset,
+						   int subquery_depth)
 {
 	if (IsA(setOp, RangeTblRef))
 	{
@@ -995,7 +1050,8 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
 		 */
 		rtr = makeNode(RangeTblRef);
 		rtr->rtindex = childRTindex;
-		(void) pull_up_subqueries(root, (Node *) rtr, NULL, appinfo);
+		(void) pull_up_subqueries(root, (Node *) rtr, NULL,
+								  appinfo, subquery_depth);
 	}
 	else if (IsA(setOp, SetOperationStmt))
 	{
@@ -1003,9 +1059,9 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex,
 
 		/* Recurse to reach leaf queries */
 		pull_up_union_leaf_queries(op->larg, root, parentRTindex, setOpQuery,
-								   childRToffset);
+								   childRToffset, subquery_depth);
 		pull_up_union_leaf_queries(op->rarg, root, parentRTindex, setOpQuery,
-								   childRToffset);
+								   childRToffset, subquery_depth);
 	}
 	else
 	{
@@ -1566,7 +1622,7 @@ flatten_simple_union_all(PlannerInfo *root)
 	 * weren't previously referenced by the jointree, and so were missed by
 	 * the main invocation of pull_up_subqueries.)
 	 */
-	pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
+	pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0, 0);
 }
 
 
@@ -2051,6 +2107,73 @@ substitute_multiple_relids(Node *node, int varno, Relids subrelids)
 }
 
 /*
+ * 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 *)node)->depth = depth;
+	}
+	else if (IsA(node, OpExpr))
+	{
+		((OpExpr *)node)->depth = depth;
+	}
+	else if (IsA(node, DistinctExpr))
+	{
+		((DistinctExpr *)node)->depth = depth;
+	}
+	else if (IsA(node, ScalarArrayOpExpr))
+	{
+		((ScalarArrayOpExpr *)node)->depth = depth;
+	}
+	else if (IsA(node, CoerceViaIO))
+	{
+		((CoerceViaIO *)node)->depth = depth;
+	}
+	else if (IsA(node, ArrayCoerceExpr))
+	{
+		((ArrayCoerceExpr *)node)->depth = depth;
+	}
+	else if (IsA(node, NullIfExpr))
+	{
+		((NullIfExpr *)node)->depth = depth;
+	}
+	else if (IsA(node, RowCompareExpr))
+	{
+		((RowCompareExpr *)node)->depth = depth;
+	}
+	return expression_tree_walker(node, mark_qualifiers_depth_walker, context);
+}
+
+static void
+mark_qualifiers_depth(Node *node, int depth)
+{
+	mark_qualifiers_depth_walker(node, &depth);
+}
+
+/*
  * fix_append_rel_relids: update RT-index fields of AppendRelInfo nodes
  *
  * When we pull up a subquery, any AppendRelInfo references to the subquery's
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index baa90fa..6df99f6 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,
@@ -2188,7 +2189,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;
 
@@ -2207,6 +2208,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))
@@ -2241,7 +2243,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;
 
@@ -2272,6 +2274,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))
@@ -2339,7 +2342,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 */
 			{
 				/*
@@ -2369,6 +2372,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))
@@ -2529,7 +2533,7 @@ eval_const_expressions_mutator(Node *node,
 								   InvalidOid,
 								   InvalidOid,
 								   &args,
-								   false, true, context);
+								   false, true, expr->depth, context);
 		if (simple)				/* successfully simplified output fn */
 		{
 			/*
@@ -2550,7 +2554,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;
 		}
@@ -2566,6 +2570,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))
@@ -2590,6 +2595,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
@@ -3406,6 +3412,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;
@@ -3436,7 +3443,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
@@ -3723,7 +3730,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);
@@ -3809,6 +3816,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 ecbbc1c..88b4826 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -43,6 +43,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/include/optimizer/prep.h b/src/include/optimizer/prep.h
index 5e94620..292ec06 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -25,7 +25,8 @@ extern void pull_up_sublinks(PlannerInfo *root);
 extern void inline_set_returning_functions(PlannerInfo *root);
 extern Node *pull_up_subqueries(PlannerInfo *root, Node *jtnode,
 				   JoinExpr *lowest_outer_join,
-				   AppendRelInfo *containing_appendrel);
+				   AppendRelInfo *containing_appendrel,
+				   int subquery_depth);
 extern void flatten_simple_union_all(PlannerInfo *root);
 extern void reduce_outer_joins(PlannerInfo *root);
 extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins);
