*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 7762,7767 ****
--- 7762,7773 ----
        <entry>Name of view's owner</entry>
       </row>
       <row>
+       <entry><structfield>security</structfield></entry>
+       <entry><type>bool</type></entry>
+       <entry></entry>
+       <entry>True, if this view has <literal>SECURITY VIEW</literal> attribute</entry>
+      </row>
+      <row>
        <entry><structfield>definition</structfield></entry>
        <entry><type>text</type></entry>
        <entry></entry>
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 66,71 **** static relopt_bool boolRelOpts[] =
--- 66,79 ----
  		},
  		true
  	},
+ 	{
+ 		{
+ 			"security_view",
+ 			"Prevent maximum optimization to prevent data leaks",
+ 			RELOPT_KIND_VIEW
+ 		},
+ 		false
+ 	},
  	/* list terminator */
  	{{NULL}}
  };
***************
*** 777,782 **** extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
--- 785,791 ----
  		case RELKIND_RELATION:
  		case RELKIND_TOASTVALUE:
  		case RELKIND_UNCATALOGED:
+ 		case RELKIND_VIEW:
  			options = heap_reloptions(classForm->relkind, datum, false);
  			break;
  		case RELKIND_INDEX:
***************
*** 1131,1137 **** default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
  		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
  		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
  		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
! 		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)}
  	};
  
  	options = parseRelOptions(reloptions, validate, kind, &numoptions);
--- 1140,1146 ----
  		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
  		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
  		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
! 		offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
  	};
  
  	options = parseRelOptions(reloptions, validate, kind, &numoptions);
***************
*** 1151,1156 **** default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
--- 1160,1195 ----
  }
  
  /*
+  * Options parser for StdViewOptions
+  */
+ bytea *
+ default_viewoptions(Datum reloptions, bool validate, relopt_kind kind)
+ {
+ 	relopt_value   *options;
+ 	StdViewOptions *viewopts;
+ 	int				numopts;
+ 	static const relopt_parse_elt tab[] = {
+ 		{"security_view", RELOPT_TYPE_BOOL,
+ 		 offsetof(StdViewOptions, security_view)},
+ 	};
+ 
+ 	options = parseRelOptions(reloptions, validate, kind, &numopts);
+ 
+ 	/* if none set, we're done */
+ 	if (numopts == 0)
+ 		return NULL;
+ 
+ 	viewopts = palloc0(sizeof(StdViewOptions));
+ 
+ 	fillRelOptions((void *) viewopts, sizeof(StdViewOptions),
+ 				   options, numopts, validate, tab, lengthof(tab));
+ 
+ 	pfree(options);
+ 
+ 	return (bytea *)viewopts;
+ }
+ 
+ /*
   * Parse options for heaps and toast tables.
   */
  bytea *
***************
*** 1173,1180 **** heap_reloptions(char relkind, Datum reloptions, bool validate)
  			return (bytea *) rdopts;
  		case RELKIND_RELATION:
  			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
  		default:
! 			/* sequences, composite types and views are not supported */
  			return NULL;
  	}
  }
--- 1212,1221 ----
  			return (bytea *) rdopts;
  		case RELKIND_RELATION:
  			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+ 		case RELKIND_VIEW:
+ 			return default_viewoptions(reloptions, validate, RELOPT_KIND_VIEW);
  		default:
! 			/* sequences and composite types are not supported */
  			return NULL;
  	}
  }
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 74,79 **** CREATE VIEW pg_views AS
--- 74,81 ----
          N.nspname AS schemaname, 
          C.relname AS viewname, 
          pg_get_userbyid(C.relowner) AS viewowner, 
+         C.reloptions IS NOT NULL
+         and '{security_view=1}' <@ C.reloptions AS security, 
          pg_get_viewdef(C.oid) AS definition 
      FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) 
      WHERE C.relkind = 'v';
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 264,270 **** static void ATRewriteTables(List **wqueue, LOCKMODE lockmode);
  static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
  static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
  static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
! static void ATSimplePermissionsRelationOrIndex(Relation rel);
  static void ATSimpleRecursion(List **wqueue, Relation rel,
  				  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
  static void ATOneLevelRecursion(List **wqueue, Relation rel,
--- 264,270 ----
  static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
  static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
  static void ATSimplePermissions(Relation rel, bool allowView, bool allowType);
! static void ATSimplePermissionsRelationOrIndex(Relation rel, bool allowView);
  static void ATSimpleRecursion(List **wqueue, Relation rel,
  				  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode);
  static void ATOneLevelRecursion(List **wqueue, Relation rel,
***************
*** 2702,2708 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			break;
  		case AT_SetOptions:		/* ALTER COLUMN SET ( options ) */
  		case AT_ResetOptions:	/* ALTER COLUMN RESET ( options ) */
! 			ATSimplePermissionsRelationOrIndex(rel);
  			/* This command never recurses */
  			pass = AT_PASS_MISC;
  			break;
--- 2702,2708 ----
  			break;
  		case AT_SetOptions:		/* ALTER COLUMN SET ( options ) */
  		case AT_ResetOptions:	/* ALTER COLUMN RESET ( options ) */
! 			ATSimplePermissionsRelationOrIndex(rel, false);
  			/* This command never recurses */
  			pass = AT_PASS_MISC;
  			break;
***************
*** 2780,2793 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			pass = AT_PASS_DROP;
  			break;
  		case AT_SetTableSpace:	/* SET TABLESPACE */
! 			ATSimplePermissionsRelationOrIndex(rel);
  			/* This command never recurses */
  			ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
  			pass = AT_PASS_MISC;	/* doesn't actually matter */
  			break;
  		case AT_SetRelOptions:	/* SET (...) */
  		case AT_ResetRelOptions:		/* RESET (...) */
! 			ATSimplePermissionsRelationOrIndex(rel);
  			/* This command never recurses */
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
--- 2780,2793 ----
  			pass = AT_PASS_DROP;
  			break;
  		case AT_SetTableSpace:	/* SET TABLESPACE */
! 			ATSimplePermissionsRelationOrIndex(rel, false);
  			/* This command never recurses */
  			ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
  			pass = AT_PASS_MISC;	/* doesn't actually matter */
  			break;
  		case AT_SetRelOptions:	/* SET (...) */
  		case AT_ResetRelOptions:		/* RESET (...) */
! 			ATSimplePermissionsRelationOrIndex(rel, true);
  			/* This command never recurses */
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
***************
*** 3584,3597 **** ATSimplePermissions(Relation rel, bool allowView, bool allowType)
   * - Ensure that it is not a system table
   */
  static void
! ATSimplePermissionsRelationOrIndex(Relation rel)
  {
  	if (rel->rd_rel->relkind != RELKIND_RELATION &&
! 		rel->rd_rel->relkind != RELKIND_INDEX)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 				 errmsg("\"%s\" is not a table or index",
! 						RelationGetRelationName(rel))));
  
  	/* Permissions checks */
  	if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
--- 3584,3599 ----
   * - Ensure that it is not a system table
   */
  static void
! ATSimplePermissionsRelationOrIndex(Relation rel, bool allowView)
  {
  	if (rel->rd_rel->relkind != RELKIND_RELATION &&
! 		rel->rd_rel->relkind != RELKIND_INDEX &&
! 		(!allowView || rel->rd_rel->relkind != RELKIND_VIEW))
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
! 				 errmsg("\"%s\" is not a table%s or index",
! 						RelationGetRelationName(rel),
! 						allowView ? ", view" : "")));
  
  	/* Permissions checks */
  	if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
***************
*** 7115,7120 **** ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
--- 7117,7123 ----
  	{
  		case RELKIND_RELATION:
  		case RELKIND_TOASTVALUE:
+ 		case RELKIND_VIEW:
  			(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
  			break;
  		case RELKIND_INDEX:
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 97,103 **** isViewOnTempTable_walker(Node *node, void *context)
   *---------------------------------------------------------------------
   */
  static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
  {
  	Oid			viewOid,
  				namespaceId;
--- 97,104 ----
   *---------------------------------------------------------------------
   */
  static Oid
! DefineVirtualRelation(const RangeVar *relation, List *tlist,
! 					  bool replace, bool security)
  {
  	Oid			viewOid,
  				namespaceId;
***************
*** 148,153 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
--- 149,156 ----
  	{
  		Relation	rel;
  		TupleDesc	descriptor;
+ 		List	   *atcmds = NIL;
+ 		AlterTableCmd  *atcmd;
  
  		/*
  		 * Yes.  Get exclusive lock on the existing view ...
***************
*** 186,205 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
  		checkViewTupleDesc(descriptor, rel->rd_att);
  
  		/*
  		 * If new attributes have been added, we must add pg_attribute entries
  		 * for them.  It is convenient (although overkill) to use the ALTER
  		 * TABLE ADD COLUMN infrastructure for this.
  		 */
  		if (list_length(attrList) > rel->rd_att->natts)
  		{
- 			List	   *atcmds = NIL;
  			ListCell   *c;
  			int			skip = rel->rd_att->natts;
  
  			foreach(c, attrList)
  			{
- 				AlterTableCmd *atcmd;
- 
  				if (skip > 0)
  				{
  					skip--;
--- 189,221 ----
  		checkViewTupleDesc(descriptor, rel->rd_att);
  
  		/*
+ 		 * If SECURITY VIEW attribute is changed, we also must update
+ 		 * corresponding reloptions.
+ 		 */
+ 		if (ViewGetSecurityFlag(rel) != security)
+ 		{
+ 			Node   *node;
+ 
+ 			node = (Node *)makeInteger(security);
+ 			node = (Node *)makeDefElem("security_view", node);
+ 			atcmd = makeNode(AlterTableCmd);
+ 			atcmd->subtype = AT_SetRelOptions;
+ 			atcmd->def = (Node *)list_make1(node);
+ 			atcmds = lappend(atcmds, atcmd);
+ 		}
+ 
+ 		/*
  		 * If new attributes have been added, we must add pg_attribute entries
  		 * for them.  It is convenient (although overkill) to use the ALTER
  		 * TABLE ADD COLUMN infrastructure for this.
  		 */
  		if (list_length(attrList) > rel->rd_att->natts)
  		{
  			ListCell   *c;
  			int			skip = rel->rd_att->natts;
  
  			foreach(c, attrList)
  			{
  				if (skip > 0)
  				{
  					skip--;
***************
*** 210,217 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
  				atcmd->def = (Node *) lfirst(c);
  				atcmds = lappend(atcmds, atcmd);
  			}
- 			AlterTableInternal(viewOid, atcmds, true);
  		}
  
  		/*
  		 * Seems okay, so return the OID of the pre-existing view.
--- 226,234 ----
  				atcmd->def = (Node *) lfirst(c);
  				atcmds = lappend(atcmds, atcmd);
  			}
  		}
+ 		if (atcmds != NIL)
+ 			AlterTableInternal(viewOid, atcmds, true);
  
  		/*
  		 * Seems okay, so return the OID of the pre-existing view.
***************
*** 237,242 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
--- 254,264 ----
  		createStmt->tablespacename = NULL;
  		createStmt->if_not_exists = false;
  
+ 		if (security)
+ 			createStmt->options = lappend(createStmt->options,
+ 										  makeDefElem("security_view",
+ 													  (Node *) makeInteger(true)));
+ 
  		/*
  		 * finally create the relation (this will error out if there's an
  		 * existing view, so we don't need more code to complain if "replace"
***************
*** 470,476 **** DefineView(ViewStmt *stmt, const char *queryString)
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 									stmt->replace);
  
  	/*
  	 * The relation we have just created is not visible to any other commands
--- 492,498 ----
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 									stmt->replace, stmt->security);
  
  	/*
  	 * The relation we have just created is not visible to any other commands
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1107,1112 **** _copyFuncExpr(FuncExpr *from)
--- 1107,1113 ----
  	COPY_SCALAR_FIELD(funcformat);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1141,1146 **** _copyOpExpr(OpExpr *from)
--- 1142,1148 ----
  	COPY_SCALAR_FIELD(opretset);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1159,1164 **** _copyDistinctExpr(DistinctExpr *from)
--- 1161,1167 ----
  	COPY_SCALAR_FIELD(opretset);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1176,1181 **** _copyScalarArrayOpExpr(ScalarArrayOpExpr *from)
--- 1179,1185 ----
  	COPY_SCALAR_FIELD(useOr);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1312,1317 **** _copyCoerceViaIO(CoerceViaIO *from)
--- 1316,1322 ----
  	COPY_SCALAR_FIELD(resulttype);
  	COPY_SCALAR_FIELD(coerceformat);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1331,1336 **** _copyArrayCoerceExpr(ArrayCoerceExpr *from)
--- 1336,1342 ----
  	COPY_SCALAR_FIELD(isExplicit);
  	COPY_SCALAR_FIELD(coerceformat);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1444,1449 **** _copyRowCompareExpr(RowCompareExpr *from)
--- 1450,1456 ----
  	COPY_NODE_FIELD(opfamilies);
  	COPY_NODE_FIELD(largs);
  	COPY_NODE_FIELD(rargs);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1514,1519 **** _copyNullIfExpr(NullIfExpr *from)
--- 1521,1527 ----
  	COPY_SCALAR_FIELD(opretset);
  	COPY_NODE_FIELD(args);
  	COPY_LOCATION_FIELD(location);
+ 	COPY_SCALAR_FIELD(depth);
  
  	return newnode;
  }
***************
*** 1671,1676 **** _copyFromExpr(FromExpr *from)
--- 1679,1685 ----
  
  	COPY_NODE_FIELD(fromlist);
  	COPY_NODE_FIELD(quals);
+ 	COPY_SCALAR_FIELD(security_view);
  
  	return newnode;
  }
***************
*** 1825,1830 **** _copyRangeTblEntry(RangeTblEntry *from)
--- 1834,1840 ----
  	COPY_SCALAR_FIELD(rtekind);
  	COPY_SCALAR_FIELD(relid);
  	COPY_NODE_FIELD(subquery);
+ 	COPY_SCALAR_FIELD(security_view);
  	COPY_SCALAR_FIELD(jointype);
  	COPY_NODE_FIELD(joinaliasvars);
  	COPY_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 238,243 **** _equalFuncExpr(FuncExpr *a, FuncExpr *b)
--- 238,244 ----
  
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 273,278 **** _equalOpExpr(OpExpr *a, OpExpr *b)
--- 274,280 ----
  	COMPARE_SCALAR_FIELD(opretset);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 297,302 **** _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b)
--- 299,305 ----
  	COMPARE_SCALAR_FIELD(opretset);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 320,325 **** _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b)
--- 323,329 ----
  	COMPARE_SCALAR_FIELD(useOr);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 434,439 **** _equalCoerceViaIO(CoerceViaIO *a, CoerceViaIO *b)
--- 438,444 ----
  		return false;
  
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 457,462 **** _equalArrayCoerceExpr(ArrayCoerceExpr *a, ArrayCoerceExpr *b)
--- 462,468 ----
  		return false;
  
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 553,558 **** _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b)
--- 559,565 ----
  	COMPARE_NODE_FIELD(opfamilies);
  	COMPARE_NODE_FIELD(largs);
  	COMPARE_NODE_FIELD(rargs);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 614,619 **** _equalNullIfExpr(NullIfExpr *a, NullIfExpr *b)
--- 621,627 ----
  	COMPARE_SCALAR_FIELD(opretset);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_LOCATION_FIELD(location);
+ 	COMPARE_SCALAR_FIELD(depth);
  
  	return true;
  }
***************
*** 730,735 **** _equalFromExpr(FromExpr *a, FromExpr *b)
--- 738,744 ----
  {
  	COMPARE_NODE_FIELD(fromlist);
  	COMPARE_NODE_FIELD(quals);
+ 	COMPARE_SCALAR_FIELD(security_view);
  
  	return true;
  }
***************
*** 2183,2188 **** _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
--- 2192,2198 ----
  	COMPARE_SCALAR_FIELD(rtekind);
  	COMPARE_SCALAR_FIELD(relid);
  	COMPARE_NODE_FIELD(subquery);
+ 	COMPARE_SCALAR_FIELD(security_view);
  	COMPARE_SCALAR_FIELD(jointype);
  	COMPARE_NODE_FIELD(joinaliasvars);
  	COMPARE_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 930,935 **** _outFuncExpr(StringInfo str, FuncExpr *node)
--- 930,936 ----
  	WRITE_ENUM_FIELD(funcformat, CoercionForm);
  	WRITE_NODE_FIELD(args);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 954,959 **** _outOpExpr(StringInfo str, OpExpr *node)
--- 955,961 ----
  	WRITE_BOOL_FIELD(opretset);
  	WRITE_NODE_FIELD(args);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 967,972 **** _outDistinctExpr(StringInfo str, DistinctExpr *node)
--- 969,975 ----
  	WRITE_BOOL_FIELD(opretset);
  	WRITE_NODE_FIELD(args);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 979,984 **** _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node)
--- 982,988 ----
  	WRITE_BOOL_FIELD(useOr);
  	WRITE_NODE_FIELD(args);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 1092,1097 **** _outCoerceViaIO(StringInfo str, CoerceViaIO *node)
--- 1096,1102 ----
  	WRITE_OID_FIELD(resulttype);
  	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 1106,1111 **** _outArrayCoerceExpr(StringInfo str, ArrayCoerceExpr *node)
--- 1111,1117 ----
  	WRITE_BOOL_FIELD(isExplicit);
  	WRITE_ENUM_FIELD(coerceformat, CoercionForm);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 1184,1189 **** _outRowCompareExpr(StringInfo str, RowCompareExpr *node)
--- 1190,1196 ----
  	WRITE_NODE_FIELD(opfamilies);
  	WRITE_NODE_FIELD(largs);
  	WRITE_NODE_FIELD(rargs);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 1234,1239 **** _outNullIfExpr(StringInfo str, NullIfExpr *node)
--- 1241,1247 ----
  	WRITE_BOOL_FIELD(opretset);
  	WRITE_NODE_FIELD(args);
  	WRITE_LOCATION_FIELD(location);
+ 	WRITE_INT_FIELD(depth);
  }
  
  static void
***************
*** 1341,1346 **** _outFromExpr(StringInfo str, FromExpr *node)
--- 1349,1355 ----
  
  	WRITE_NODE_FIELD(fromlist);
  	WRITE_NODE_FIELD(quals);
+ 	WRITE_BOOL_FIELD(security_view);
  }
  
  /*****************************************************************************
***************
*** 2124,2129 **** _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
--- 2133,2139 ----
  			break;
  		case RTE_SUBQUERY:
  			WRITE_NODE_FIELD(subquery);
+ 			WRITE_BOOL_FIELD(security_view);
  			break;
  		case RTE_JOIN:
  			WRITE_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 526,531 **** _readFuncExpr(void)
--- 526,532 ----
  	READ_ENUM_FIELD(funcformat, CoercionForm);
  	READ_NODE_FIELD(args);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 571,576 **** _readOpExpr(void)
--- 572,578 ----
  	READ_BOOL_FIELD(opretset);
  	READ_NODE_FIELD(args);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 600,605 **** _readDistinctExpr(void)
--- 602,608 ----
  	READ_BOOL_FIELD(opretset);
  	READ_NODE_FIELD(args);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 628,633 **** _readScalarArrayOpExpr(void)
--- 631,637 ----
  	READ_BOOL_FIELD(useOr);
  	READ_NODE_FIELD(args);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 740,745 **** _readCoerceViaIO(void)
--- 744,750 ----
  	READ_OID_FIELD(resulttype);
  	READ_ENUM_FIELD(coerceformat, CoercionForm);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 759,764 **** _readArrayCoerceExpr(void)
--- 764,770 ----
  	READ_BOOL_FIELD(isExplicit);
  	READ_ENUM_FIELD(coerceformat, CoercionForm);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 872,877 **** _readRowCompareExpr(void)
--- 878,884 ----
  	READ_NODE_FIELD(opfamilies);
  	READ_NODE_FIELD(largs);
  	READ_NODE_FIELD(rargs);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 953,958 **** _readNullIfExpr(void)
--- 960,966 ----
  	READ_BOOL_FIELD(opretset);
  	READ_NODE_FIELD(args);
  	READ_LOCATION_FIELD(location);
+ 	READ_INT_FIELD(depth);
  
  	READ_DONE();
  }
***************
*** 1110,1115 **** _readFromExpr(void)
--- 1118,1124 ----
  
  	READ_NODE_FIELD(fromlist);
  	READ_NODE_FIELD(quals);
+ 	READ_BOOL_FIELD(security_view);
  
  	READ_DONE();
  }
***************
*** 1140,1145 **** _readRangeTblEntry(void)
--- 1149,1155 ----
  			break;
  		case RTE_SUBQUERY:
  			READ_NODE_FIELD(subquery);
+ 			READ_BOOL_FIELD(security_view);
  			break;
  		case RTE_JOIN:
  			READ_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 2456,2461 **** cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root)
--- 2456,2462 ----
  	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 ... */
  
***************
*** 2481,2486 **** cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root)
--- 2482,2488 ----
  	context.root = root;
  	context.total.startup = 0;
  	context.total.per_tuple = 0;
+ 	context.total.depth = 0;
  
  	cost_qual_eval_walker(qual, &context);
  
***************
*** 2510,2515 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2512,2518 ----
  			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
***************
*** 2534,2539 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2537,2544 ----
  		}
  		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;
  	}
***************
*** 2562,2567 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2567,2574 ----
  	{
  		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) ||
***************
*** 2571,2576 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2578,2585 ----
  		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))
  	{
***************
*** 2584,2589 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2593,2600 ----
  		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, CoerceViaIO))
  	{
***************
*** 2600,2605 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2611,2618 ----
  		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))
  	{
***************
*** 2609,2614 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2622,2629 ----
  		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))
  	{
***************
*** 2623,2628 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
--- 2638,2645 ----
  			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))
  	{
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 2443,2448 **** order_qual_clauses(PlannerInfo *root, List *clauses)
--- 2443,2449 ----
  	{
  		Node	   *clause;
  		Cost		cost;
+ 		int			depth;
  	} QualItem;
  	int			nitems = list_length(clauses);
  	QualItem   *items;
***************
*** 2468,2473 **** order_qual_clauses(PlannerInfo *root, List *clauses)
--- 2469,2475 ----
  		cost_qual_eval_node(&qcost, clause, root);
  		items[i].clause = clause;
  		items[i].cost = qcost.per_tuple;
+ 		items[i].depth = qcost.depth;
  		i++;
  	}
  
***************
*** 2484,2490 **** 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)
  				break;
  			items[j] = items[j - 1];
  		}
--- 2486,2494 ----
  		/* insert newitem into the already-sorted subarray */
  		for (j = i; j > 0; j--)
  		{
! 			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];
  		}
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
***************
*** 40,46 **** int			join_collapse_limit;
  
  static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
  					bool below_outer_join,
! 					Relids *qualscope, Relids *inner_join_rels);
  static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
  				   Relids left_rels, Relids right_rels,
  				   Relids inner_join_rels,
--- 40,47 ----
  
  static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
  					bool below_outer_join,
! 					Relids *qualscope, Relids *inner_join_rels,
! 					bool below_sec_barriers, Relids *sec_barriers);
  static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
  				   Relids left_rels, Relids right_rels,
  				   Relids inner_join_rels,
***************
*** 51,57 **** static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
  						JoinType jointype,
  						Relids qualscope,
  						Relids ojscope,
! 						Relids outerjoin_nonnullable);
  static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
  					  Relids *nullable_relids_p, bool is_pushed_down);
  static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
--- 52,59 ----
  						JoinType jointype,
  						Relids qualscope,
  						Relids ojscope,
! 						Relids outerjoin_nonnullable,
! 						Relids sec_barriers);
  static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
  					  Relids *nullable_relids_p, bool is_pushed_down);
  static bool check_redundant_nullability_qual(PlannerInfo *root, Node *clause);
***************
*** 238,250 **** deconstruct_jointree(PlannerInfo *root)
  {
  	Relids		qualscope;
  	Relids		inner_join_rels;
  
  	/* Start recursion at top of jointree */
! 	Assert(root->parse->jointree != NULL &&
! 		   IsA(root->parse->jointree, FromExpr));
  
! 	return deconstruct_recurse(root, (Node *) root->parse->jointree, false,
! 							   &qualscope, &inner_join_rels);
  }
  
  /*
--- 240,254 ----
  {
  	Relids		qualscope;
  	Relids		inner_join_rels;
+ 	Relids		sec_barriers;
+ 	FromExpr   *f = (FromExpr *)root->parse->jointree;
  
  	/* Start recursion at top of jointree */
! 	Assert(root->parse->jointree != NULL && IsA(f, FromExpr));
  
! 	return deconstruct_recurse(root, (Node *) f, false,
! 							   &qualscope, &inner_join_rels,
! 							   f->security_view, &sec_barriers);
  }
  
  /*
***************
*** 268,274 **** deconstruct_jointree(PlannerInfo *root)
   */
  static List *
  deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
! 					Relids *qualscope, Relids *inner_join_rels)
  {
  	List	   *joinlist;
  
--- 272,279 ----
   */
  static List *
  deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
! 					Relids *qualscope, Relids *inner_join_rels,
! 					bool below_sec_barriers, Relids *sec_barriers)
  {
  	List	   *joinlist;
  
***************
*** 287,292 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 292,300 ----
  		/* A single baserel does not create an inner join */
  		*inner_join_rels = NULL;
  		joinlist = list_make1(jtnode);
+ 		/* Is it in security barrier? */
+ 		*sec_barriers = (below_sec_barriers ?
+ 						 bms_make_singleton(varno) : NULL);
  	}
  	else if (IsA(jtnode, FromExpr))
  	{
***************
*** 302,307 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 310,316 ----
  		 */
  		*qualscope = NULL;
  		*inner_join_rels = NULL;
+ 		*sec_barriers = NULL;
  		joinlist = NIL;
  		remaining = list_length(f->fromlist);
  		foreach(l, f->fromlist)
***************
*** 309,320 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
  			Relids		sub_qualscope;
  			List	   *sub_joinlist;
  			int			sub_members;
  
  			sub_joinlist = deconstruct_recurse(root, lfirst(l),
  											   below_outer_join,
  											   &sub_qualscope,
! 											   inner_join_rels);
  			*qualscope = bms_add_members(*qualscope, sub_qualscope);
  			sub_members = list_length(sub_joinlist);
  			remaining--;
  			if (sub_members <= 1 ||
--- 318,334 ----
  			Relids		sub_qualscope;
  			List	   *sub_joinlist;
  			int			sub_members;
+ 			Relids		sub_barriers;
  
  			sub_joinlist = deconstruct_recurse(root, lfirst(l),
  											   below_outer_join,
  											   &sub_qualscope,
! 											   inner_join_rels,
! 											   below_sec_barriers ?
! 											   true : f->security_view,
! 											   &sub_barriers);
  			*qualscope = bms_add_members(*qualscope, sub_qualscope);
+ 			*sec_barriers = bms_add_members(*sec_barriers, sub_barriers);
  			sub_members = list_length(sub_joinlist);
  			remaining--;
  			if (sub_members <= 1 ||
***************
*** 343,349 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
  
  			distribute_qual_to_rels(root, qual,
  									false, below_outer_join, JOIN_INNER,
! 									*qualscope, NULL, NULL);
  		}
  	}
  	else if (IsA(jtnode, JoinExpr))
--- 357,363 ----
  
  			distribute_qual_to_rels(root, qual,
  									false, below_outer_join, JOIN_INNER,
! 									*qualscope, NULL, NULL, *sec_barriers);
  		}
  	}
  	else if (IsA(jtnode, JoinExpr))
***************
*** 353,358 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
--- 367,374 ----
  					rightids,
  					left_inners,
  					right_inners,
+ 					left_barriers,
+ 					right_barriers,
  					nonnullable_rels,
  					ojscope;
  		List	   *leftjoinlist,
***************
*** 377,388 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
  			case JOIN_INNER:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													below_outer_join,
! 													&rightids, &right_inners);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = *qualscope;
  				/* Inner join adds no restrictions for quals */
  				nonnullable_rels = NULL;
  				break;
--- 393,409 ----
  			case JOIN_INNER:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners,
! 												   below_sec_barriers,
! 												   &left_barriers);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													below_outer_join,
! 													&rightids, &right_inners,
! 													below_sec_barriers,
! 													&right_barriers);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = *qualscope;
+ 				*sec_barriers = bms_union(left_barriers, right_barriers);
  				/* Inner join adds no restrictions for quals */
  				nonnullable_rels = NULL;
  				break;
***************
*** 390,424 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
  			case JOIN_ANTI:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													true,
! 													&rightids, &right_inners);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
  				nonnullable_rels = leftids;
  				break;
  			case JOIN_SEMI:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													below_outer_join,
! 													&rightids, &right_inners);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
  				/* Semi join adds no restrictions for quals */
  				nonnullable_rels = NULL;
  				break;
  			case JOIN_FULL:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   true,
! 												   &leftids, &left_inners);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													true,
! 													&rightids, &right_inners);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
  				/* each side is both outer and inner */
  				nonnullable_rels = *qualscope;
  				break;
--- 411,460 ----
  			case JOIN_ANTI:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners,
! 												   below_sec_barriers,
! 												   &left_barriers);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													true,
! 													&rightids, &right_inners,
! 													below_sec_barriers,
! 													&right_barriers);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
+ 				*sec_barriers = bms_union(left_barriers, right_barriers);
  				nonnullable_rels = leftids;
  				break;
  			case JOIN_SEMI:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   below_outer_join,
! 												   &leftids, &left_inners,
! 												   below_sec_barriers,
! 												   &left_barriers);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													below_outer_join,
! 													&rightids, &right_inners,
! 													below_sec_barriers,
! 													&right_barriers);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
+ 				*sec_barriers = bms_union(left_barriers, right_barriers);
  				/* Semi join adds no restrictions for quals */
  				nonnullable_rels = NULL;
  				break;
  			case JOIN_FULL:
  				leftjoinlist = deconstruct_recurse(root, j->larg,
  												   true,
! 												   &leftids, &left_inners,
! 												   below_sec_barriers,
! 												   &left_barriers);
  				rightjoinlist = deconstruct_recurse(root, j->rarg,
  													true,
! 													&rightids, &right_inners,
! 													below_sec_barriers,
! 													&right_barriers);
  				*qualscope = bms_union(leftids, rightids);
  				*inner_join_rels = bms_union(left_inners, right_inners);
+ 				*sec_barriers = bms_union(left_barriers, right_barriers);
  				/* each side is both outer and inner */
  				nonnullable_rels = *qualscope;
  				break;
***************
*** 467,473 **** deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
  			distribute_qual_to_rels(root, qual,
  									false, below_outer_join, j->jointype,
  									*qualscope,
! 									ojscope, nonnullable_rels);
  		}
  
  		/* Now we can add the SpecialJoinInfo to join_info_list */
--- 503,510 ----
  			distribute_qual_to_rels(root, qual,
  									false, below_outer_join, j->jointype,
  									*qualscope,
! 									ojscope, nonnullable_rels,
! 									*sec_barriers);
  		}
  
  		/* Now we can add the SpecialJoinInfo to join_info_list */
***************
*** 791,797 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
  						JoinType jointype,
  						Relids qualscope,
  						Relids ojscope,
! 						Relids outerjoin_nonnullable)
  {
  	Relids		relids;
  	bool		is_pushed_down;
--- 828,835 ----
  						JoinType jointype,
  						Relids qualscope,
  						Relids ojscope,
! 						Relids outerjoin_nonnullable,
! 						Relids sec_barriers)
  {
  	Relids		relids;
  	bool		is_pushed_down;
***************
*** 799,804 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 837,843 ----
  	bool		pseudoconstant = false;
  	bool		maybe_equivalence;
  	bool		maybe_outer_join;
+ 	bool		maybe_leakable_clause = false;
  	Relids		nullable_relids;
  	RestrictInfo *restrictinfo;
  
***************
*** 815,820 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 854,861 ----
  		elog(ERROR, "JOIN qualification cannot refer to other relations");
  	if (ojscope && !bms_is_subset(relids, ojscope))
  		elog(ERROR, "JOIN qualification cannot refer to other relations");
+ 	if (sec_barriers && !bms_is_subset(sec_barriers, qualscope))
+ 		elog(ERROR, "Bug? incorrect security barriers");
  
  	/*
  	 * If the clause is variable-free, our normal heuristic for pushing it
***************
*** 871,876 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
--- 912,931 ----
  		}
  	}
  
+ 	/*
+ 	 * If the clause contains a leakable function, it may be used to
+ 	 * bypass row-level security using views. In this case, we don't
+ 	 * push down the clause not to evaluate the leakable clause prior
+ 	 * to the row-level policy functions.
+ 	 */
+ 	if (!bms_is_empty(sec_barriers) &&
+ 		contain_leakable_functions(clause) &&
+ 		bms_overlap(relids, sec_barriers))
+ 	{
+ 		maybe_leakable_clause = true;
+ 		relids = bms_add_members(relids, sec_barriers);
+ 	}
+ 
  	/*----------
  	 * Check to see if clause application must be delayed by outer-join
  	 * considerations.
***************
*** 1067,1073 **** distribute_qual_to_rels(PlannerInfo *root, Node *clause,
  	 * If none of the above hold, pass it off to
  	 * distribute_restrictinfo_to_rels().
  	 */
! 	if (restrictinfo->mergeopfamilies)
  	{
  		if (maybe_equivalence)
  		{
--- 1122,1128 ----
  	 * If none of the above hold, pass it off to
  	 * distribute_restrictinfo_to_rels().
  	 */
! 	if (!maybe_leakable_clause && restrictinfo->mergeopfamilies)
  	{
  		if (maybe_equivalence)
  		{
***************
*** 1396,1402 **** process_implied_equality(PlannerInfo *root,
  	 */
  	distribute_qual_to_rels(root, (Node *) clause,
  							true, below_outer_join, JOIN_INNER,
! 							qualscope, NULL, NULL);
  }
  
  /*
--- 1451,1457 ----
  	 */
  	distribute_qual_to_rels(root, (Node *) clause,
  							true, below_outer_join, JOIN_INNER,
! 							qualscope, NULL, NULL, NULL);
  }
  
  /*
*** a/src/backend/optimizer/prep/prepjointree.c
--- b/src/backend/optimizer/prep/prepjointree.c
***************
*** 845,850 **** pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
--- 845,856 ----
  	 */
  
  	/*
+ 	 * If the subquery is originated from security view, we mark it here
+ 	 * to prevent over optimization later.
+ 	 */
+ 	((FromExpr *) subquery->jointree)->security_view = rte->security_view;
+ 
+ 	/*
  	 * Return the adjusted subquery jointree to replace the RangeTblRef entry
  	 * in parent's jointree.
  	 */
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 86,91 **** static bool contain_subplans_walker(Node *node, void *context);
--- 86,92 ----
  static bool contain_mutable_functions_walker(Node *node, void *context);
  static bool contain_volatile_functions_walker(Node *node, void *context);
  static bool contain_nonstrict_functions_walker(Node *node, void *context);
+ static bool contain_leakable_functions_walker(Node *node, void *context);
  static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
  static List *find_nonnullable_vars_walker(Node *node, bool top_level);
  static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
***************
*** 103,108 **** static Expr *simplify_function(Oid funcid,
--- 104,110 ----
  				  Oid result_type, int32 result_typmod, 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,
***************
*** 115,121 **** 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, List *args,
! 				  HeapTuple func_tuple,
  				  eval_const_expressions_context *context);
  static Expr *inline_function(Oid funcid, Oid result_type, List *args,
  				HeapTuple func_tuple,
--- 117,123 ----
  						   HeapTuple func_tuple);
  static Expr *evaluate_function(Oid funcid,
  				  Oid result_type, int32 result_typmod, List *args,
! 				  HeapTuple func_tuple, int depth,
  				  eval_const_expressions_context *context);
  static Expr *inline_function(Oid funcid, Oid result_type, List *args,
  				HeapTuple func_tuple,
***************
*** 1129,1134 **** contain_nonstrict_functions_walker(Node *node, void *context)
--- 1131,1250 ----
  }
  
  
+ 
+ /*****************************************************************************
+  *		Check clauses for leakable functions
+  *****************************************************************************/
+ 
+ /*
+  * contain_leakable_functions
+  *	  Recursively search for leakable functions within a clause.
+  *
+  * Returns true if any function call with side-effect is found.
+  * ie, some type-input/output handler will raise an error when given
+  *     argument does not have a valid format.
+  *
+  * When people uses views for row-level security purpose, given qualifiers
+  * come from outside of the view should not be pushed down into the views,
+  * if they have side-effect, because contents of tuples to be filtered out
+  * may be leaked via side-effectable functions within the qualifiers.
+  * 
+  * The idea here is that the planner restrain a part of optimization when
+  * the qualifiers contains leakable functions.
+  * This routine checks whether the given clause contains leakable functions,
+  * or not. If we return false, then the clause is clean.
+  */
+ bool
+ contain_leakable_functions(Node *clause)
+ {
+ 	return contain_leakable_functions_walker(clause, NULL);
+ }
+ 
+ static bool
+ contain_leakable_functions_walker(Node *node, void *context)
+ {
+ 	if (node == NULL)
+ 		return false;
+ 
+ 	if (IsA(node, FuncExpr))
+ 	{
+ 		/*
+ 		 * currently, we have no way to distinguish a safe function and
+ 		 * a leakable one, so all the function call shall be considered
+ 		 * as leakable one.
+ 		 */
+ 		return true;
+ 	}
+ 	else if (IsA(node, OpExpr))
+ 	{
+ 		OpExpr	   *expr = (OpExpr *) node;
+ 
+ 		/*
+ 		 * we assume built-in functions to implement operators are not
+ 		 * leakable, so don't need to prevent optimization.
+ 		 */
+ 		set_opfuncid(expr);
+ 		if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ 			return true;
+ 		/* else fall through to check args */
+ 	}
+ 	else if (IsA(node, DistinctExpr))
+ 	{
+ 		DistinctExpr *expr = (DistinctExpr *) node;
+ 
+ 		set_opfuncid((OpExpr *) expr);
+ 		if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ 			return true;
+ 		/* else fall through to check args */
+ 	}
+ 	else if (IsA(node, ScalarArrayOpExpr))
+ 	{
+ 		ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
+ 
+ 		set_sa_opfuncid(expr);
+ 		if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ 			return true;
+ 		/* else fall through to check args */
+ 	}
+ 	else if (IsA(node, CoerceViaIO) ||
+ 			 IsA(node, ArrayCoerceExpr))
+ 	{
+ 		/*
+ 		 * we assume type-in/out handlers are leakable, even if built-in
+ 		 * functions. 
+ 		 * ie, int4in() raises an error message with given argument,
+ 		 *     if it does not have valid format for numeric value.
+ 		 */
+ 		return true;
+ 	}
+ 	else if (IsA(node, NullIfExpr))
+ 	{
+ 		NullIfExpr *expr = (NullIfExpr *) node;
+ 
+ 		set_opfuncid((OpExpr *) expr);	/* rely on struct equivalence */
+ 		if (get_func_lang(expr->opfuncid) != INTERNALlanguageId)
+ 			return true;
+ 		/* else fall through to check args */
+ 	}
+ 	else if (IsA(node, RowCompareExpr))
+ 	{
+ 		/* RowCompare probably can't have volatile ops, but check anyway */
+ 		RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ 		ListCell   *opid;
+ 
+ 		foreach(opid, rcexpr->opnos)
+ 		{
+ 			Oid		funcId = get_opcode(lfirst_oid(opid));
+ 
+ 			if (get_func_lang(funcId) != INTERNALlanguageId)
+ 				return true;
+ 		}
+ 		/* else fall through to check args */
+ 	}
+ 	return expression_tree_walker(node, contain_leakable_functions_walker,
+ 								  context);
+ }
+ 
  /*
   * find_nonnullable_rels
   *		Determine which base rels are forced nonnullable by given clause.
***************
*** 2169,2175 **** eval_const_expressions_mutator(Node *node,
  		simple = simplify_function(expr->funcid,
  								   expr->funcresulttype, exprTypmod(node),
  								   &args,
! 								   has_named_args, true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
  
--- 2285,2291 ----
  		simple = simplify_function(expr->funcid,
  								   expr->funcresulttype, exprTypmod(node),
  								   &args,
! 								   has_named_args, true, expr->depth, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
  
***************
*** 2186,2191 **** eval_const_expressions_mutator(Node *node,
--- 2302,2308 ----
  		newexpr->funcformat = expr->funcformat;
  		newexpr->args = args;
  		newexpr->location = expr->location;
+ 		newexpr->depth = expr->depth;
  		return (Node *) newexpr;
  	}
  	if (IsA(node, OpExpr))
***************
*** 2217,2223 **** eval_const_expressions_mutator(Node *node,
  		simple = simplify_function(expr->opfuncid,
  								   expr->opresulttype, -1,
  								   &args,
! 								   false, true, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
  
--- 2334,2340 ----
  		simple = simplify_function(expr->opfuncid,
  								   expr->opresulttype, -1,
  								   &args,
! 								   false, true, expr->depth, context);
  		if (simple)				/* successfully simplified it */
  			return (Node *) simple;
  
***************
*** 2246,2251 **** eval_const_expressions_mutator(Node *node,
--- 2363,2369 ----
  		newexpr->opretset = expr->opretset;
  		newexpr->args = args;
  		newexpr->location = expr->location;
+ 		newexpr->depth = expr->depth;
  		return (Node *) newexpr;
  	}
  	if (IsA(node, DistinctExpr))
***************
*** 2310,2316 **** eval_const_expressions_mutator(Node *node,
  			simple = simplify_function(expr->opfuncid,
  									   expr->opresulttype, -1,
  									   &args,
! 									   false, false, context);
  			if (simple)			/* successfully simplified it */
  			{
  				/*
--- 2428,2434 ----
  			simple = simplify_function(expr->opfuncid,
  									   expr->opresulttype, -1,
  									   &args,
! 									   false, false, expr->depth, context);
  			if (simple)			/* successfully simplified it */
  			{
  				/*
***************
*** 2338,2343 **** eval_const_expressions_mutator(Node *node,
--- 2456,2462 ----
  		newexpr->opretset = expr->opretset;
  		newexpr->args = args;
  		newexpr->location = expr->location;
+ 		newexpr->depth = expr->depth;
  		return (Node *) newexpr;
  	}
  	if (IsA(node, BoolExpr))
***************
*** 2502,2508 **** eval_const_expressions_mutator(Node *node,
  		simple = simplify_function(outfunc,
  								   CSTRINGOID, -1,
  								   &args,
! 								   false, true, context);
  		if (simple)				/* successfully simplified output fn */
  		{
  			/*
--- 2621,2627 ----
  		simple = simplify_function(outfunc,
  								   CSTRINGOID, -1,
  								   &args,
! 								   false, true, expr->depth, context);
  		if (simple)				/* successfully simplified output fn */
  		{
  			/*
***************
*** 2520,2526 **** eval_const_expressions_mutator(Node *node,
  			simple = simplify_function(infunc,
  									   expr->resulttype, -1,
  									   &args,
! 									   false, true, context);
  			if (simple)			/* successfully simplified input fn */
  				return (Node *) simple;
  		}
--- 2639,2645 ----
  			simple = simplify_function(infunc,
  									   expr->resulttype, -1,
  									   &args,
! 									   false, true, expr->depth, context);
  			if (simple)			/* successfully simplified input fn */
  				return (Node *) simple;
  		}
***************
*** 2535,2540 **** eval_const_expressions_mutator(Node *node,
--- 2654,2660 ----
  		newexpr->resulttype = expr->resulttype;
  		newexpr->coerceformat = expr->coerceformat;
  		newexpr->location = expr->location;
+ 		newexpr->depth = expr->depth;
  		return (Node *) newexpr;
  	}
  	if (IsA(node, ArrayCoerceExpr))
***************
*** 2558,2563 **** eval_const_expressions_mutator(Node *node,
--- 2678,2684 ----
  		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
***************
*** 3292,3297 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3413,3419 ----
  				  List **args,
  				  bool has_named_args,
  				  bool allow_inline,
+ 				  int depth,
  				  eval_const_expressions_context *context)
  {
  	HeapTuple	func_tuple;
***************
*** 3320,3326 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
  		*args = add_function_defaults(*args, result_type, func_tuple, context);
  
  	newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
! 								func_tuple, context);
  
  	if (!newexpr && allow_inline)
  		newexpr = inline_function(funcid, result_type, *args,
--- 3442,3448 ----
  		*args = add_function_defaults(*args, result_type, func_tuple, context);
  
  	newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
! 								func_tuple, depth, context);
  
  	if (!newexpr && allow_inline)
  		newexpr = inline_function(funcid, result_type, *args,
***************
*** 3571,3577 **** recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
   */
  static Expr *
  evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
! 				  HeapTuple func_tuple,
  				  eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
--- 3693,3699 ----
   */
  static Expr *
  evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
! 				  HeapTuple func_tuple, int depth,
  				  eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
***************
*** 3654,3659 **** evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args,
--- 3776,3782 ----
  	newexpr->funcformat = COERCE_DONTCARE;		/* doesn't matter */
  	newexpr->args = args;
  	newexpr->location = -1;
+ 	newexpr->depth = depth;
  
  	return evaluate_expr((Expr *) newexpr, result_type, result_typmod);
  }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 311,317 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
  %type <fun_param_mode> arg_class
  %type <typnam>	func_return func_type
  
! %type <boolean>  TriggerForType OptTemp
  %type <oncommit> OnCommitOption
  
  %type <node>	for_locking_item
--- 311,317 ----
  %type <fun_param_mode> arg_class
  %type <typnam>	func_return func_type
  
! %type <boolean>  TriggerForType OptTemp OptSecurity
  %type <oncommit> OnCommitOption
  
  %type <node>	for_locking_item
***************
*** 6494,6528 **** transaction_mode_list_or_empty:
  /*****************************************************************************
   *
   *	QUERY:
!  *		CREATE [ OR REPLACE ] [ TEMP ] VIEW <viewname> '('target-list ')'
   *			AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $4;
  					n->view->istemp = $2;
! 					n->aliases = $5;
! 					n->query = $7;
  					n->replace = false;
  					$$ = (Node *) n;
  				}
! 		| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $6;
  					n->view->istemp = $4;
! 					n->aliases = $7;
! 					n->query = $9;
  					n->replace = true;
  					$$ = (Node *) n;
  				}
  		;
  
  opt_check_option:
  		WITH CHECK OPTION
  				{
--- 6494,6536 ----
  /*****************************************************************************
   *
   *	QUERY:
!  *		CREATE [ OR REPLACE ] [ TEMP ] [ SECURITY ] VIEW
!  *			<viewname> '('target-list ')'
   *			AS <query> [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp OptSecurity VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $5;
  					n->view->istemp = $2;
! 					n->aliases = $6;
! 					n->query = $8;
  					n->replace = false;
+ 					n->security = $3;
  					$$ = (Node *) n;
  				}
! 		| CREATE OR REPLACE OptTemp OptSecurity
! 				VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
! 					n->view = $7;
  					n->view->istemp = $4;
! 					n->aliases = $8;
! 					n->query = $10;
  					n->replace = true;
+ 					n->security = $5;
  					$$ = (Node *) n;
  				}
  		;
  
+ OptSecurity:	SECURITY			{ $$ = TRUE; }
+ 		|		/* EMPTY */			{ $$ = FALSE; }
+ 		;
+ 
  opt_check_option:
  		WITH CHECK OPTION
  				{
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 1132,1137 **** matchLocks(CmdType event,
--- 1132,1251 ----
  	return matching_locks;
  }
  
+ /*
+  * MarkFuncExprDepth
+  *
+  * It marks the depth field of each expression node that may eventually
+  * call functions. On the checks of WHERE clause, every qualifiers are
+  * sorted by cost estimation, then a qualifier with lower-cost shall
+  * be evaluated ealier than expensive one.
+  * However, this logic can cause a problem when simple subqueries are
+  * pull-up to join clauses. If a view is used to row-level security
+  * purpose, its security policy function should be evaluated earlier
+  * than any functions supplied by users, because they may have side-
+  * effects that leaks given arguments somewhere, alhough it is contents
+  * of tuples to be filtered out.
+  * So, we need to ensure a function originated from inner subquery
+  * being evaluated earlier than functions originated from outer
+  * subqueries.
+  */
+ static bool
+ MarkFuncExprDepthWalker(Node *node, void *context)
+ {
+ 	int		depth = (int) (intptr_t) 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;
+ 	}
+ 	else if (IsA(node, Query))
+ 	{
+ 		query_tree_walker((Query *)node,
+ 						  MarkFuncExprDepthWalker,
+ 						  (void *) (intptr_t) (depth + 1), 0);
+ 		return false;
+ 	}
+ 	return expression_tree_walker(node, MarkFuncExprDepthWalker, context);
+ }
+ 
+ static void
+ MarkFuncExprDepth(List *query_list)
+ {
+ 	ListCell   *l;
+ 
+ 	foreach (l, query_list)
+ 	{
+ 		Node   *node = lfirst(l);
+ 
+ 		Assert(IsA(node, Query));
+ 
+ 		query_tree_walker((Query *)node,
+ 						  MarkFuncExprDepthWalker,
+ 						  (void *) 1, 0);
+ 	}
+ }
  
  /*
   * ApplyRetrieveRule - expand an ON SELECT rule
***************
*** 1187,1192 **** ApplyRetrieveRule(Query *parsetree,
--- 1301,1308 ----
  	rte->relid = InvalidOid;
  	rte->subquery = rule_action;
  	rte->inh = false;			/* must not be set for a subquery */
+ 	if (ViewGetSecurityFlag(relation))
+ 		rte->security_view = true;
  
  	/*
  	 * We move the view's permission check data down to its rangetable. The
***************
*** 1961,1965 **** QueryRewrite(Query *parsetree)
--- 2077,2086 ----
  	if (!foundOriginalQuery && lastInstead != NULL)
  		lastInstead->canSetTag = true;
  
+ 	/*
+ 	 * Mark the ->depth fields of expression nodes
+ 	 */
+ 	MarkFuncExprDepth(results);
+ 
  	return results;
  }
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 1275,1280 **** get_func_namespace(Oid funcid)
--- 1275,1299 ----
  }
  
  /*
+  * get_func_lang
+  *		Given procedure id, return the function's language
+  */
+ Oid
+ get_func_lang(Oid funcid)
+ {
+ 	HeapTuple	tp;
+ 	Oid			result;
+ 
+ 	tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+ 	if (!HeapTupleIsValid(tp))
+ 		elog(ERROR, "cache lookup failed for function %u", funcid);
+ 
+ 	result = ((Form_pg_proc) GETSTRUCT(tp))->prolang;
+ 	ReleaseSysCache(tp);
+ 	return result;
+ }
+ 
+ /*
   * get_func_rettype
   *		Given procedure id, return the function's result type.
   */
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 384,389 **** RelationParseRelOptions(Relation relation, HeapTuple tuple)
--- 384,390 ----
  		case RELKIND_RELATION:
  		case RELKIND_TOASTVALUE:
  		case RELKIND_INDEX:
+ 		case RELKIND_VIEW:
  			break;
  		default:
  			return;
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 10882,10890 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
--- 10882,10896 ----
  	if (tbinfo->relkind == RELKIND_VIEW)
  	{
  		char	   *viewdef;
+ 		bool		secview = false;
  
  		reltypename = "VIEW";
  
+ 		/* check SECURITY VIEW attribute */
+ 		if (tbinfo->reloptions &&
+ 			strstr(tbinfo->reloptions, "security_view=1") != NULL)
+ 			secview = true;
+ 
  		/* Fetch the view definition */
  		if (g_fout->remoteVersion >= 70300)
  		{
***************
*** 10936,10942 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
  		if (binary_upgrade)
  			binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
  
! 		appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
  						  fmtId(tbinfo->dobj.name), viewdef);
  
  		PQclear(res);
--- 10942,10949 ----
  		if (binary_upgrade)
  			binary_upgrade_set_relfilenodes(q, tbinfo->dobj.catId.oid, false);
  
! 		appendPQExpBuffer(q, "CREATE %sVIEW %s AS\n    %s\n",
! 						  (secview ? "SECURITY " : ""),
  						  fmtId(tbinfo->dobj.name), viewdef);
  
  		PQclear(res);
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2394,2399 **** listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
--- 2394,2409 ----
  		appendPQExpBuffer(&buf,
  						  ",\n  pg_catalog.pg_size_pretty(pg_catalog.pg_relation_size(c.oid)) as \"%s\"",
  						  gettext_noop("Size"));
+ 	if (verbose && showViews && pset.sversion >= 90100)
+ 		appendPQExpBuffer(&buf, ",\n"
+ 						  "CASE WHEN c.relkind = 'v'"
+ 						  "  THEN CASE WHEN '{security_view=1}' <@ c.reloptions"
+ 						  "         THEN 'true'"
+ 						  "         ELSE 'false'"
+ 						  "       END"
+ 						  "  ELSE null"
+ 						  "  END AS \"%s\"",
+ 						  gettext_noop("Security"));
  	if (verbose)
  		appendPQExpBuffer(&buf,
  			  ",\n  pg_catalog.obj_description(c.oid, 'pg_class') as \"%s\"",
*** a/src/include/access/reloptions.h
--- b/src/include/access/reloptions.h
***************
*** 42,49 **** typedef enum relopt_kind
  	RELOPT_KIND_GIST = (1 << 5),
  	RELOPT_KIND_ATTRIBUTE = (1 << 6),
  	RELOPT_KIND_TABLESPACE = (1 << 7),
  	/* if you add a new kind, make sure you update "last_default" too */
! 	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_TABLESPACE,
  	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
  	RELOPT_KIND_MAX = (1 << 30)
  } relopt_kind;
--- 42,50 ----
  	RELOPT_KIND_GIST = (1 << 5),
  	RELOPT_KIND_ATTRIBUTE = (1 << 6),
  	RELOPT_KIND_TABLESPACE = (1 << 7),
+ 	RELOPT_KIND_VIEW = (1 << 8),
  	/* if you add a new kind, make sure you update "last_default" too */
! 	RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW,
  	/* some compilers treat enums as signed ints, so we can't use 1 << 31 */
  	RELOPT_KIND_MAX = (1 << 30)
  } relopt_kind;
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 684,689 **** typedef struct RangeTblEntry
--- 684,691 ----
  	 */
  	Query	   *subquery;		/* the sub-query */
  
+ 	bool		security_view;	/* Is the sub-query come from security view? */
+ 
  	/*
  	 * Fields valid for a join RTE (else NULL/zero):
  	 *
***************
*** 2201,2206 **** typedef struct ViewStmt
--- 2203,2209 ----
  	List	   *aliases;		/* target column names */
  	Node	   *query;			/* the SELECT query */
  	bool		replace;		/* replace an existing view? */
+ 	bool		security;		/* the view for row-level security? */
  } ViewStmt;
  
  /* ----------------------
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 325,330 **** typedef struct FuncExpr
--- 325,331 ----
  	CoercionForm funcformat;	/* how to display this function call */
  	List	   *args;			/* arguments to the function */
  	int			location;		/* token location, or -1 if unknown */
+ 	int			depth;			/* depth of clause in the original query */
  } FuncExpr;
  
  /*
***************
*** 368,373 **** typedef struct OpExpr
--- 369,375 ----
  	bool		opretset;		/* true if operator returns set */
  	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;
  
  /*
***************
*** 400,405 **** typedef struct ScalarArrayOpExpr
--- 402,408 ----
  	bool		useOr;			/* true for ANY, false for ALL */
  	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;
  
  /*
***************
*** 659,664 **** typedef struct CoerceViaIO
--- 662,668 ----
  	/* output typmod is not stored, but is presumed -1 */
  	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;
  
  /* ----------------
***************
*** 683,688 **** typedef struct ArrayCoerceExpr
--- 687,693 ----
  	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;
  
  /* ----------------
***************
*** 852,857 **** typedef struct RowCompareExpr
--- 857,863 ----
  	List	   *opfamilies;		/* OID list of containing operator families */
  	List	   *largs;			/* the left-hand input arguments */
  	List	   *rargs;			/* the right-hand input arguments */
+ 	int			depth;			/* depth of clause in the original query */
  } RowCompareExpr;
  
  /*
***************
*** 1208,1213 **** typedef struct FromExpr
--- 1214,1220 ----
  	NodeTag		type;
  	List	   *fromlist;		/* List of join subtrees */
  	Node	   *quals;			/* qualifiers on join, if any */
+ 	bool		security_view;	/* It came from security views */
  } FromExpr;
  
  #endif   /* PRIMNODES_H */
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 44,49 **** typedef struct QualCost
--- 44,50 ----
  {
  	Cost		startup;		/* one-time cost */
  	Cost		per_tuple;		/* per-evaluation cost */
+ 	int			depth;			/* depth of qual in the original query */
  } QualCost;
  
  
*** a/src/include/optimizer/clauses.h
--- b/src/include/optimizer/clauses.h
***************
*** 67,72 **** extern bool contain_subplans(Node *clause);
--- 67,73 ----
  extern bool contain_mutable_functions(Node *clause);
  extern bool contain_volatile_functions(Node *clause);
  extern bool contain_nonstrict_functions(Node *clause);
+ extern bool contain_leakable_functions(Node *clause);
  extern Relids find_nonnullable_rels(Node *clause);
  extern List *find_nonnullable_vars(Node *clause);
  extern List *find_forced_null_vars(Node *clause);
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
***************
*** 77,82 **** extern RegProcedure get_oprrest(Oid opno);
--- 77,83 ----
  extern RegProcedure get_oprjoin(Oid opno);
  extern char *get_func_name(Oid funcid);
  extern Oid	get_func_namespace(Oid funcid);
+ extern Oid	get_func_lang(Oid funcid);
  extern Oid	get_func_rettype(Oid funcid);
  extern int	get_func_nargs(Oid funcid);
  extern Oid	get_func_signature(Oid funcid, Oid **argtypes, int *nargs);
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 270,275 **** typedef struct StdRdOptions
--- 270,293 ----
  	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
  
  /*
+  * StdViewOptions
+  *        Standard contents of rd_options for views
+  */
+ typedef struct StdViewOptions
+ {
+ 	int32		vl_len_;		/* varlena header (do not touch directly!) */
+ 	bool		security_view;	/* view has SECURITY VIEW attribute? */
+ } StdViewOptions;
+ 
+ /*
+  * ViewGetSecurityFlag
+  *     Returns the view's security flag
+  */
+ #define ViewGetSecurityFlag(relation)								\
+ 	((relation)->rd_options ?										\
+ 	 ((StdViewOptions *) (relation)->rd_options)->security_view : false)
+ 
+ /*
   * RelationIsValid
   *		True iff relation descriptor is valid.
   */
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
***************
*** 1319,1325 **** SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
   pg_timezone_names           | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst);
   pg_user                     | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow;
   pg_user_mappings            | SELECT u.oid AS umid, s.oid AS srvid, s.srvname, u.umuser, CASE WHEN (u.umuser = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename, CASE WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions ELSE NULL::text[] END AS umoptions FROM ((pg_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.umuser))) JOIN pg_foreign_server s ON ((u.umserver = s.oid)));
!  pg_views                    | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char");
   rtest_v1                    | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1;
   rtest_vcomp                 | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit);
   rtest_vview1                | SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
--- 1319,1325 ----
   pg_timezone_names           | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst);
   pg_user                     | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow;
   pg_user_mappings            | SELECT u.oid AS umid, s.oid AS srvid, s.srvname, u.umuser, CASE WHEN (u.umuser = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename, CASE WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions ELSE NULL::text[] END AS umoptions FROM ((pg_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.umuser))) JOIN pg_foreign_server s ON ((u.umserver = s.oid)));
!  pg_views                    | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, ((c.reloptions IS NOT NULL) AND ('{security_view=1}'::text[] <@ c.reloptions)) AS security, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char");
   rtest_v1                    | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1;
   rtest_vcomp                 | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit);
   rtest_vview1                | SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a)));
*** /dev/null
--- b/src/test/regress/expected/security_views.out
***************
*** 0 ****
--- 1,133 ----
+ --
+ -- Clean up in case a prior regression run failed
+ --
+ SET client_min_messages TO 'warning';
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ DROP VIEW IF EXISTS v1;
+ DROP VIEW IF EXISTS v2;
+ DROP VIEW IF EXISTS v3;
+ DROP FUNCTION IF EXISTS f_policy(int);
+ DROP FUNCTION IF EXISTS f_malicious(text);
+ RESET client_min_messages;
+ --
+ -- Test for SECURITY VIEWs
+ --
+ CREATE OR REPLACE FUNCTION f_policy(int)
+     RETURNS bool LANGUAGE 'plpgsql'
+     AS 'BEGIN RETURN $1 % 2 = 0; END';
+ CREATE OR REPLACE FUNCTION f_malicious(text)
+     RETURNS bool COST 0.0001 LANGUAGE 'plpgsql'
+     AS 'BEGIN RAISE NOTICE ''leak: %'', $1; RETURN true; END';
+ CREATE TABLE t1 (a int primary key, b text);
+ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
+ COPY t1 FROM stdin;
+ CREATE TABLE t2 (x int primary key, y text);
+ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t2_pkey" for table "t2"
+ COPY t2 FROM stdin;
+ CREATE OR REPLACE SECURITY VIEW v1 AS
+ 	SELECT * FROM t1 WHERE f_policy(a);
+ CREATE OR REPLACE VIEW v2 AS
+ 	SELECT * FROM t1 JOIN t2 ON t1.a = t2.x WHERE f_policy(t1.a);
+ CREATE OR REPLACE SECURITY VIEW v3 AS
+ 	SELECT * FROM t1 JOIN t2 ON t1.a = t2.x WHERE f_policy(t1.a);
+ SELECT viewname, security FROM pg_views WHERE viewname in ('v1', 'v2', 'v3');
+  viewname | security 
+ ----------+----------
+  v1       | t
+  v2       | f
+  v3       | t
+ (3 rows)
+ 
+ -- f_malicious() is earlier than f_policy()
+ SELECT * FROM t1 WHERE f_policy(a) AND f_malicious(b);
+ NOTICE:  leak: aaa
+ NOTICE:  leak: bbb
+ NOTICE:  leak: ccc
+ NOTICE:  leak: ddd
+ NOTICE:  leak: eee
+  a |  b  
+ ---+-----
+  2 | bbb
+  4 | ddd
+ (2 rows)
+ 
+ EXPLAIN SELECT * FROM t1 WHERE f_policy(a) AND f_malicious(b);
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Seq Scan on t1  (cost=0.00..329.80 rows=137 width=36)
+    Filter: (f_malicious(b) AND f_policy(a))
+ (2 rows)
+ 
+ -- f_policy() is earlier than f_malicious()
+ SELECT * FROM v1 WHERE f_malicious(b);
+ NOTICE:  leak: bbb
+ NOTICE:  leak: ddd
+  a |  b  
+ ---+-----
+  2 | bbb
+  4 | ddd
+ (2 rows)
+ 
+ EXPLAIN SELECT * FROM v1 WHERE f_malicious(b);
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Seq Scan on t1  (cost=0.00..329.80 rows=137 width=36)
+    Filter: (f_policy(a) AND f_malicious(b))
+ (2 rows)
+ 
+ -- f_malicious() can be pushed down into inside of the join loop
+ SELECT * FROM v2 WHERE f_malicious(y);
+ NOTICE:  leak: vvv
+ NOTICE:  leak: www
+ NOTICE:  leak: xxx
+ NOTICE:  leak: yyy
+ NOTICE:  leak: zzz
+  a |  b  | x |  y  
+ ---+-----+---+-----
+  2 | bbb | 2 | www
+  4 | ddd | 4 | yyy
+ (2 rows)
+ 
+ EXPLAIN SELECT * FROM v2 WHERE f_malicious(y);
+                                QUERY PLAN                                
+ -------------------------------------------------------------------------
+  Nested Loop  (cost=0.00..287.63 rows=410 width=72)
+    ->  Seq Scan on t2  (cost=0.00..22.30 rows=410 width=36)
+          Filter: f_malicious(y)
+    ->  Index Scan using t1_pkey on t1  (cost=0.00..0.63 rows=1 width=36)
+          Index Cond: (a = t2.x)
+          Filter: f_policy(a)
+ (6 rows)
+ 
+ -- f_malicious() is executed outside of the join loop
+ SELECT * FROM v3 WHERE f_malicious(y);
+ NOTICE:  leak: www
+ NOTICE:  leak: yyy
+  a |  b  | x |  y  
+ ---+-----+---+-----
+  2 | bbb | 2 | www
+  4 | ddd | 4 | yyy
+ (2 rows)
+ 
+ EXPLAIN SELECT * FROM v3 WHERE f_malicious(y);
+                             QUERY PLAN                             
+ -------------------------------------------------------------------
+  Hash Join  (cost=37.68..384.39 rows=137 width=72)
+    Hash Cond: (t1.a = t2.x)
+    Join Filter: (f_policy(t1.a) AND f_malicious(t2.y))
+    ->  Seq Scan on t1  (cost=0.00..22.30 rows=1230 width=36)
+    ->  Hash  (cost=22.30..22.30 rows=1230 width=36)
+          ->  Seq Scan on t2  (cost=0.00..22.30 rows=1230 width=36)
+ (6 rows)
+ 
+ --
+ -- Cleanups
+ --
+ DROP VIEW v1;
+ DROP VIEW v2;
+ DROP VIEW v3;
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP FUNCTION f_policy(int);
+ DROP FUNCTION f_malicious(text);
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 84,90 **** test: rules
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps
  
  # ----------
  # Another group of parallel tests
--- 84,90 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: select_views security_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps
  
  # ----------
  # Another group of parallel tests
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 92,97 **** test: security_label
--- 92,98 ----
  test: misc
  test: rules
  test: select_views
+ test: security_views
  test: portals_p2
  test: foreign_key
  test: cluster
*** /dev/null
--- b/src/test/regress/sql/security_views.sql
***************
*** 0 ****
--- 1,80 ----
+ --
+ -- Clean up in case a prior regression run failed
+ --
+ SET client_min_messages TO 'warning';
+ 
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ DROP VIEW IF EXISTS v1;
+ DROP VIEW IF EXISTS v2;
+ DROP VIEW IF EXISTS v3;
+ DROP FUNCTION IF EXISTS f_policy(int);
+ DROP FUNCTION IF EXISTS f_malicious(text);
+ 
+ RESET client_min_messages;
+ 
+ --
+ -- Test for SECURITY VIEWs
+ --
+ CREATE OR REPLACE FUNCTION f_policy(int)
+     RETURNS bool LANGUAGE 'plpgsql'
+     AS 'BEGIN RETURN $1 % 2 = 0; END';
+ 
+ CREATE OR REPLACE FUNCTION f_malicious(text)
+     RETURNS bool COST 0.0001 LANGUAGE 'plpgsql'
+     AS 'BEGIN RAISE NOTICE ''leak: %'', $1; RETURN true; END';
+ 
+ CREATE TABLE t1 (a int primary key, b text);
+ COPY t1 FROM stdin;
+ 1	aaa
+ 2	bbb
+ 3	ccc
+ 4	ddd
+ 5	eee
+ \.
+ 
+ CREATE TABLE t2 (x int primary key, y text);
+ COPY t2 FROM stdin;
+ 1	vvv
+ 2	www
+ 3	xxx
+ 4	yyy
+ 5	zzz
+ \.
+ 
+ CREATE OR REPLACE SECURITY VIEW v1 AS
+ 	SELECT * FROM t1 WHERE f_policy(a);
+ CREATE OR REPLACE VIEW v2 AS
+ 	SELECT * FROM t1 JOIN t2 ON t1.a = t2.x WHERE f_policy(t1.a);
+ CREATE OR REPLACE SECURITY VIEW v3 AS
+ 	SELECT * FROM t1 JOIN t2 ON t1.a = t2.x WHERE f_policy(t1.a);
+ 
+ SELECT viewname, security FROM pg_views WHERE viewname in ('v1', 'v2', 'v3');
+ 
+ -- f_malicious() is earlier than f_policy()
+ SELECT * FROM t1 WHERE f_policy(a) AND f_malicious(b);
+ EXPLAIN SELECT * FROM t1 WHERE f_policy(a) AND f_malicious(b);
+ 
+ -- f_policy() is earlier than f_malicious()
+ SELECT * FROM v1 WHERE f_malicious(b);
+ EXPLAIN SELECT * FROM v1 WHERE f_malicious(b);
+ 
+ -- f_malicious() can be pushed down into inside of the join loop
+ SELECT * FROM v2 WHERE f_malicious(y);
+ EXPLAIN SELECT * FROM v2 WHERE f_malicious(y);
+ 
+ -- f_malicious() is executed outside of the join loop
+ SELECT * FROM v3 WHERE f_malicious(y);
+ EXPLAIN SELECT * FROM v3 WHERE f_malicious(y);
+ 
+ --
+ -- Cleanups
+ --
+ DROP VIEW v1;
+ DROP VIEW v2;
+ DROP VIEW v3;
+ DROP TABLE t1;
+ DROP TABLE t2;
+ 
+ DROP FUNCTION f_policy(int);
+ DROP FUNCTION f_malicious(text);
