*** a/doc/src/sgml/ref/alter_view.sgml
--- b/doc/src/sgml/ref/alter_view.sgml
***************
*** 142,147 **** ALTER VIEW [ IF EXISTS ] <replaceable class="parameter">name</replaceable> RESET
--- 142,157 ----
           </para>
          </listitem>
         </varlistentry>
+        <varlistentry>
+         <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+         <listitem>
+          <para>
+           Changes the security-definer property of the view.  The value must
+           be Boolean value, such as <literal>true</literal>
+           or <literal>false</literal>.
+          </para>
+         </listitem>
+        </varlistentry>
        </variablelist>
       </para>
      </listitem>
*** a/doc/src/sgml/ref/create_view.sgml
--- b/doc/src/sgml/ref/create_view.sgml
***************
*** 139,145 **** CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
         </varlistentry>
  
         <varlistentry>
!         <term><literal>security_barrier</literal> (<type>string</type>)</term>
          <listitem>
           <para>
            This should be used if the view is intended to provide row-level
--- 139,145 ----
         </varlistentry>
  
         <varlistentry>
!         <term><literal>security_barrier</literal> (<type>boolean</type>)</term>
          <listitem>
           <para>
            This should be used if the view is intended to provide row-level
***************
*** 147,152 **** CREATE VIEW <replaceable>name</> AS WITH RECURSIVE <replaceable>name</> (<replac
--- 147,162 ----
           </para>
          </listitem>
         </varlistentry>
+        
+        <varlistentry>
+         <term><literal>security_definer</literal> (<type>boolean</type>)</term>
+         <listitem>
+          <para>
+           This should be used if the view is intended to be executed with
+           owner privileges rather than the current user.
+          </para>
+         </listitem>
+        </varlistentry>
        </variablelist>
       </para>
      </listitem>
***************
*** 276,281 **** CREATE VIEW vista AS SELECT text 'Hello World' AS hello;
--- 286,322 ----
      to replace it (this includes being a member of the owning role).
     </para>
  
+   <refsect2 id="SQL-CREATEVIEW-security-definer-views">
+    <title id="SQL-CREATEVIEW-security-definer-views-title">Security definer Views</title>
+ 
+    <indexterm zone="sql-createview-security-definer-views">
+     <primary>security definer views</primary>
+    </indexterm>
+ 
+    <para>
+     Security definer views uses the view owner id instead of the current user
+     in the following conditions, otherwise the current user is used to verify
+     the privileges and etc.
+     <itemizedlist>
+      <listitem>
+       <para>
+        The view is used in <command>INSERT</>, <command>UPDATE</>
+        and <command>DELETE</> statements in the same way as on a
+        regular table.
+       </para>
+      </listitem>
+ 
+      <listitem>
+       <para>
+        To apply row-level security policies on the underlying base
+        relations of the view, based on the security definer, the 
+        corresponding policies related to the user are applied.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </refsect2>
+  
    <refsect2 id="SQL-CREATEVIEW-updatable-views">
     <title id="SQL-CREATEVIEW-updatable-views-title">Updatable Views</title>
  
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 89,94 **** static relopt_bool boolRelOpts[] =
--- 89,103 ----
  		},
  		false
  	},
+ 	{
+ 		{
+ 			"security_definer",
+ 			"specifies that the view is to be executed with the privileges of the user that created it.",
+ 			RELOPT_KIND_VIEW,
+ 			AccessExclusiveLock
+ 		},
+ 		false
+ 	},
  	/* list terminator */
  	{{NULL}}
  };
***************
*** 1320,1325 **** view_reloptions(Datum reloptions, bool validate)
--- 1329,1336 ----
  	static const relopt_parse_elt tab[] = {
  		{"security_barrier", RELOPT_TYPE_BOOL,
  		offsetof(ViewOptions, security_barrier)},
+ 		{ "security_definer", RELOPT_TYPE_BOOL,
+ 		offsetof(ViewOptions, security_definer) },
  		{"check_option", RELOPT_TYPE_STRING,
  		offsetof(ViewOptions, check_option_offset)}
  	};
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "catalog/pg_type.h"
  #include "commands/trigger.h"
  #include "foreign/fdwapi.h"
+ #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/analyze.h"
***************
*** 1616,1621 **** fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
--- 1617,1623 ----
  	int			origResultRelation = parsetree->resultRelation;
  	int			rt_index;
  	ListCell   *lc;
+ 	bool		security_definer_view = false;
  
  	/*
  	 * don't try to convert this into a foreach loop, because rtable list can
***************
*** 1777,1794 **** fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
  		++rt_index;
  
  		/* Only normal relations can have RLS policies */
! 		if (rte->rtekind != RTE_RELATION ||
! 			rte->relkind != RELKIND_RELATION)
  			continue;
! 
  		rel = heap_open(rte->relid, NoLock);
  
  		/*
  		 * Fetch any new security quals that must be applied to this RTE.
  		 */
  		get_row_security_policies(parsetree, rte, rt_index,
  								  &securityQuals, &withCheckOptions,
! 								  &hasRowSecurity, &hasSubLinks);
  
  		if (securityQuals != NIL || withCheckOptions != NIL)
  		{
--- 1779,1805 ----
  		++rt_index;
  
  		/* Only normal relations can have RLS policies */
! 		if (rte->rtekind != RTE_RELATION)
  			continue;
! 		
  		rel = heap_open(rte->relid, NoLock);
  
+ 		if (rte->relkind == RELKIND_VIEW && RelationIsSecurityDefinerView(rel))
+ 			security_definer_view = true;
+ 
+ 		if (rte->relkind != RELKIND_RELATION)
+ 		{
+ 			heap_close(rel, NoLock);
+ 			continue;
+ 		}
+ 
  		/*
  		 * Fetch any new security quals that must be applied to this RTE.
  		 */
  		get_row_security_policies(parsetree, rte, rt_index,
  								  &securityQuals, &withCheckOptions,
! 								  &hasRowSecurity, &hasSubLinks,
! 								  security_definer_view);
  
  		if (securityQuals != NIL || withCheckOptions != NIL)
  		{
***************
*** 2824,2830 **** rewriteTargetView(Query *parsetree, Relation view)
  	 * the executor still performs appropriate permissions checks for the
  	 * query caller's use of the view.
  	 */
! 	new_rte->checkAsUser = view->rd_rel->relowner;
  	new_rte->requiredPerms = view_rte->requiredPerms;
  
  	/*
--- 2835,2844 ----
  	 * the executor still performs appropriate permissions checks for the
  	 * query caller's use of the view.
  	 */
! 	if (RelationIsSecurityDefinerView(view))
! 		new_rte->checkAsUser = view->rd_rel->relowner;
! 	else
! 		new_rte->checkAsUser = GetUserId();
  	new_rte->requiredPerms = view_rte->requiredPerms;
  
  	/*
*** a/src/backend/rewrite/rowsecurity.c
--- b/src/backend/rewrite/rowsecurity.c
***************
*** 105,111 **** row_security_policy_hook_type row_security_policy_hook_restrictive = NULL;
  void
  get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks)
  {
  	Oid			user_id;
  	int			rls_status;
--- 105,112 ----
  void
  get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks,
! 						  bool security_definer_view)
  {
  	Oid			user_id;
  	int			rls_status;
***************
*** 125,134 **** get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
  		return;
  
  	/* Switch to checkAsUser if it's set */
! 	user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
  
  	/* Determine the state of RLS for this, pass checkAsUser explicitly */
! 	rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false);
  
  	/* If there is no RLS on this table at all, nothing to do */
  	if (rls_status == RLS_NONE)
--- 126,138 ----
  		return;
  
  	/* Switch to checkAsUser if it's set */
! 	if (security_definer_view)
! 		user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId();
! 	else
! 		user_id = GetUserId();
  
  	/* Determine the state of RLS for this, pass checkAsUser explicitly */
! 	rls_status = check_enable_rls(rte->relid, user_id, false);
  
  	/* If there is no RLS on this table at all, nothing to do */
  	if (rls_status == RLS_NONE)
*** a/src/include/rewrite/rowsecurity.h
--- b/src/include/rewrite/rowsecurity.h
***************
*** 43,48 **** extern PGDLLIMPORT row_security_policy_hook_type row_security_policy_hook_restri
  extern void get_row_security_policies(Query *root,
  						  RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks);
  
  #endif   /* ROWSECURITY_H */
--- 43,49 ----
  extern void get_row_security_policies(Query *root,
  						  RangeTblEntry *rte, int rt_index,
  						  List **securityQuals, List **withCheckOptions,
! 						  bool *hasRowSecurity, bool *hasSubLinks,
! 						  bool security_definer_view);
  
  #endif   /* ROWSECURITY_H */
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 265,270 **** typedef struct ViewOptions
--- 265,271 ----
  {
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
  	bool		security_barrier;
+ 	bool		security_definer;
  	int			check_option_offset;
  } ViewOptions;
  
***************
*** 278,283 **** typedef struct ViewOptions
--- 279,292 ----
  	 ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
  
  /*
+  * RelationIsSecurityDefiner
+  *		Returns whether the relation is security definer, or not.
+  */
+ #define RelationIsSecurityDefinerView(relation)	\
+ 	((relation)->rd_options ?				\
+ 	 ((ViewOptions *) (relation)->rd_options)->security_definer : false)
+ 
+ /*
   * RelationHasCheckOption
   *		Returns true if the relation is a view defined with either the local
   *		or the cascaded check option.  Note multiple eval of argument!
*** a/src/test/regress/expected/rowsecurity.out
--- b/src/test/regress/expected/rowsecurity.out
***************
*** 1425,1431 **** CREATE POLICY p1 ON b1 USING (a % 2 = 0);
  ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  SET SESSION AUTHORIZATION rls_regress_user2;
  EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
--- 1425,1431 ----
  ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  SET SESSION AUTHORIZATION rls_regress_user2;
  EXPLAIN (COSTS OFF) SELECT * FROM bv1 WHERE f_leak(b);
***************
*** 1740,1746 **** EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  -- Query as role that is not owner of view or table.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user1;
--- 1740,1746 ----
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  -- Query as role that is not owner of view or table.  Should return all records.
  SET SESSION AUTHORIZATION rls_regress_user1;
***************
*** 1789,1795 **** EXPLAIN (COSTS OFF) SELECT * FROM rls_view;
  DROP VIEW rls_view;
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  -- Query as role that is not owner of view but is owner of table.
  -- Should return records based on view owner policies.
--- 1789,1795 ----
  DROP VIEW rls_view;
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  -- Query as role that is not owner of view but is owner of table.
  -- Should return records based on view owner policies.
*** a/src/test/regress/expected/updatable_views.out
--- b/src/test/regress/expected/updatable_views.out
***************
*** 966,972 **** CREATE USER view_user2;
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  GRANT SELECT ON base_tbl TO view_user2;
  GRANT SELECT ON rw_view1 TO view_user2;
--- 966,972 ----
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  GRANT SELECT ON base_tbl TO view_user2;
  GRANT SELECT ON rw_view1 TO view_user2;
*** a/src/test/regress/sql/rowsecurity.sql
--- b/src/test/regress/sql/rowsecurity.sql
***************
*** 491,497 **** ALTER TABLE b1 ENABLE ROW LEVEL SECURITY;
  GRANT ALL ON b1 TO rls_regress_user1;
  
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  
  SET SESSION AUTHORIZATION rls_regress_user2;
--- 491,497 ----
  GRANT ALL ON b1 TO rls_regress_user1;
  
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW bv1 WITH (security_barrier, security_definer) AS SELECT * FROM b1 WHERE a > 0 WITH CHECK OPTION;
  GRANT ALL ON bv1 TO rls_regress_user2;
  
  SET SESSION AUTHORIZATION rls_regress_user2;
***************
*** 665,671 **** EXPLAIN (COSTS OFF) SELECT * FROM z1 WHERE f_leak(b);
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  
  -- Query as role that is not owner of view or table.  Should return all records.
--- 665,671 ----
  --
  -- View and Table owner are the same.
  SET SESSION AUTHORIZATION rls_regress_user0;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user1;
  
  -- Query as role that is not owner of view or table.  Should return all records.
***************
*** 681,687 **** DROP VIEW rls_view;
  
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  
  -- Query as role that is not owner of view but is owner of table.
--- 681,687 ----
  
  -- View and Table owners are different.
  SET SESSION AUTHORIZATION rls_regress_user1;
! CREATE VIEW rls_view WITH (security_definer) AS SELECT * FROM z1 WHERE f_leak(b);
  GRANT SELECT ON rls_view TO rls_regress_user0;
  
  -- Query as role that is not owner of view but is owner of table.
*** a/src/test/regress/sql/updatable_views.sql
--- b/src/test/regress/sql/updatable_views.sql
***************
*** 397,403 **** CREATE USER view_user2;
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  
  GRANT SELECT ON base_tbl TO view_user2;
--- 397,403 ----
  SET SESSION AUTHORIZATION view_user1;
  CREATE TABLE base_tbl(a int, b text, c float);
  INSERT INTO base_tbl VALUES (1, 'Row 1', 1.0);
! CREATE VIEW rw_view1 WITH (security_definer) AS SELECT b AS bb, c AS cc, a AS aa FROM base_tbl;
  INSERT INTO rw_view1 VALUES ('Row 2', 2.0, 2);
  
  GRANT SELECT ON base_tbl TO view_user2;
