The attached patch is a revised one; that utilizes untransformRelOptions()
to construct a list of DefElem to be supplied into AT_ResetRelOptions
commands. It enabled me to implement more compact as I expected.

How about this approach to reset existing reloptions?

I'll consolidate part-0, 1 and 2 patches after we make fix the direction to
distinguish leaky qualifiers from others, in the thread of part-2.
Right now, I'm considering the right way to choose qualifiers to be
transformed into index scans.

Thanks,

2011/7/7 Noah Misch <n...@2ndquadrant.com>:
> On Thu, Jul 07, 2011 at 03:56:26PM +0100, Kohei KaiGai wrote:
>> 2011/7/7 Noah Misch <n...@2ndquadrant.com>:
>> > On Wed, Jul 06, 2011 at 10:25:12PM +0200, Kohei KaiGai wrote:
>> >> *** a/src/backend/commands/view.c
>> >> --- b/src/backend/commands/view.c
>> >
>> >> --- 227,257 ----
>> >>                               atcmd->def = (Node *) lfirst(c);
>> >>                               atcmds = lappend(atcmds, atcmd);
>> >>                       }
>> >>               }
>> >>
>> >>               /*
>> >> +              * If optional parameters are specified, we must set options
>> >> +              * using ALTER TABLE SET OPTION internally.
>> >> +              */
>> >> +             if (list_length(options) > 0)
>> >> +             {
>> >> +                     atcmd = makeNode(AlterTableCmd);
>> >> +                     atcmd->subtype = AT_SetRelOptions;
>> >> +                     atcmd->def = (List *)options;
>> >> +
>> >> +                     atcmds = lappend(atcmds, atcmd);
>> >> +             }
>> >> +             else
>> >> +             {
>> >> +                     atcmd = makeNode(AlterTableCmd);
>> >> +                     atcmd->subtype = AT_ResetRelOptions;
>> >> +                     atcmd->def = (Node *) 
>> >> list_make1(makeDefElem("security_barrier",
>> >> +                                                                         
>> >>                                      NULL));
>> >> +             }
>> >> +             if (atcmds != NIL)
>> >> +                     AlterTableInternal(viewOid, atcmds, true);
>> >> +
>> >> +             /*
>> >>                * Seems okay, so return the OID of the pre-existing view.
>> >>                */
>> >>               relation_close(rel, NoLock);    /* keep the lock! */
>> >
>> > That gets the job done for today, but DefineVirtualRelation() should not 
>> > need
>> > to know all view options by name to simply replace the existing list with a
>> > new one.  I don't think you can cleanly use the ALTER TABLE SET/RESET code 
>> > for
>> > this.  Instead, compute an option list similar to how DefineRelation() 
>> > does so
>> > at tablecmds.c:491, then update pg_class.
>> >
>> My opinion is ALTER TABLE SET/RESET code should be enhanced to accept
>> an operation to reset all the existing options, rather than tricky
>> updates of pg_class.
>
> The pg_class update has ~20 lines of idiomatic code; see 
> tablecmds.c:7931-7951.
>
>> How about an idea to add AT_ResetAllRelOptions for internal use only?
>
> If some operation is purely internal and does not otherwise benefit from the
> ALTER TABLE infrastructure, there's no benefit in involving ALTER TABLE.
> DefineVirtualRelation() uses ALTER TABLE to add columns because all that code
> needs to exist anyway.  You could make a plain function to do the update that
> gets called from both ATExecSetRelOptions() and DefineVirtualRelation().
>
> Thanks,
> nm
>
-- 
KaiGai Kohei <kai...@kaigai.gr.jp>
*** a/doc/src/sgml/ref/alter_view.sgml
--- b/doc/src/sgml/ref/alter_view.sgml
***************
*** 26,31 **** ALTER VIEW <replaceable class="parameter">name</replaceable> ALTER [ COLUMN ] <r
--- 26,34 ----
  ALTER VIEW <replaceable class="parameter">name</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
  ALTER VIEW <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
  ALTER VIEW <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+ ALTER VIEW <replaceable class="parameter">name</replaceable> SET ( <replaceable class="parameter">parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] )
+ ALTER VIEW <replaceable class="parameter">name</replaceable> RESET ( <replaceable class="parameter">parameter</replaceable> [, ... ] )
+ 
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 102,107 **** ALTER VIEW <replaceable class="parameter">name</replaceable> SET SCHEMA <replace
--- 105,128 ----
       </para>
      </listitem>
     </varlistentry>
+    
+    <varlistentry>
+     <term><replaceable class="parameter">parameter</replaceable></term>
+     <listitem>
+      <para>
+       Name of the view option to be set.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">value</replaceable></term>
+     <listitem>
+      <para>
+       The new value for the view option.
+      </para>
+     </listitem>
+    </varlistentry>
    </variablelist>
   </refsect1>
  
*** a/doc/src/sgml/ref/create_view.sgml
--- b/doc/src/sgml/ref/create_view.sgml
***************
*** 22,27 **** PostgreSQL documentation
--- 22,28 ----
   <refsynopsisdiv>
  <synopsis>
  CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
+     [ WITH ( <replaceable class="PARAMETER">parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
      AS <replaceable class="PARAMETER">query</replaceable>
  </synopsis>
   </refsynopsisdiv>
***************
*** 99,104 **** CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
--- 100,128 ----
     </varlistentry>
  
     <varlistentry>
+     <term><literal>WITH (<replaceable class="parameter">parameter [= value]</replaceable>)</literal></term>
+     <listitem>
+      <para>
+       This clause allows to specify optional parameters for a view.
+      </para>
+      <para>
+       If <literal>security_barrier=TRUE</literal> is specified, this view
+       shall performs as security barrier that prevent unexpected information
+       leaks. It is a recommendable configuration when the view is defined
+       to apply row-level security, in spite of performance trade-off.
+      </para>
+      <para>
+       It is a commonly-used technique that using views to filter out
+       tuple to be invisible to particular users, however, please note
+       that here is a known-problem that allows malicious users to
+       reference invisible tuples using a function with side-effect
+       because of interaction with query optimization.
+       See <xref linkend="rules-privileges"> for more detailed scenario.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="parameter">query</replaceable></term>
      <listitem>
       <para>
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 66,71 **** static relopt_bool boolRelOpts[] =
--- 66,79 ----
  		},
  		true
  	},
+ 	{
+ 		{
+ 			"security_barrier",
+ 			"Prevent information leaks using functions with side-effects",
+ 			RELOPT_KIND_VIEW
+ 		},
+ 		false
+ 	},
  	/* list terminator */
  	{{NULL}}
  };
***************
*** 776,781 **** extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
--- 784,790 ----
  	{
  		case RELKIND_RELATION:
  		case RELKIND_TOASTVALUE:
+ 		case RELKIND_VIEW:
  		case RELKIND_UNCATALOGED:
  			options = heap_reloptions(classForm->relkind, datum, false);
  			break;
***************
*** 1134,1140 **** 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);
--- 1143,1151 ----
  		{"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)},
! 		{"security_barrier", RELOPT_TYPE_BOOL,
! 		 offsetof(StdRdOptions, security_barrier)},
  	};
  
  	options = parseRelOptions(reloptions, validate, kind, &numoptions);
***************
*** 1176,1181 **** heap_reloptions(char relkind, Datum reloptions, bool validate)
--- 1187,1194 ----
  			return (bytea *) rdopts;
  		case RELKIND_RELATION:
  			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
+ 		case RELKIND_VIEW:
+ 			return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);
  		default:
  			/* other relkinds are not supported */
  			return NULL;
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 2955,2961 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
  			break;
  		case AT_SetRelOptions:	/* SET (...) */
  		case AT_ResetRelOptions:		/* RESET (...) */
! 			ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
  			/* This command never recurses */
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
--- 2955,2961 ----
  			break;
  		case AT_SetRelOptions:	/* SET (...) */
  		case AT_ResetRelOptions:		/* RESET (...) */
! 			ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX | ATT_VIEW);
  			/* This command never recurses */
  			/* No command-specific prep needed */
  			pass = AT_PASS_MISC;
***************
*** 7911,7916 **** ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
--- 7911,7917 ----
  	{
  		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
***************
*** 15,20 ****
--- 15,21 ----
  #include "postgres.h"
  
  #include "access/heapam.h"
+ #include "access/reloptions.h"
  #include "access/xact.h"
  #include "catalog/namespace.h"
  #include "commands/defrem.h"
***************
*** 32,37 ****
--- 33,39 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
+ #include "utils/syscache.h"
  
  
  static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc);
***************
*** 98,104 **** isViewOnTempTable_walker(Node *node, void *context)
   */
  static Oid
  DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
! 					  Oid namespaceId)
  {
  	Oid			viewOid;
  	CreateStmt *createStmt = makeNode(CreateStmt);
--- 100,106 ----
   */
  static Oid
  DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
! 					  Oid namespaceId, List *options)
  {
  	Oid			viewOid;
  	CreateStmt *createStmt = makeNode(CreateStmt);
***************
*** 166,171 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
--- 168,178 ----
  	{
  		Relation	rel;
  		TupleDesc	descriptor;
+ 		List	   *atcmds = NIL;
+ 		AlterTableCmd *atcmd;
+ 		HeapTuple	tuple;
+ 		Datum		datum;
+ 		bool		isnull;
  
  		/*
  		 * Yes.  Get exclusive lock on the existing view ...
***************
*** 210,223 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
  		 */
  		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--;
--- 217,227 ----
***************
*** 228,237 **** 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.
  		 */
  		relation_close(rel, NoLock);	/* keep the lock! */
--- 232,281 ----
  				atcmd->def = (Node *) lfirst(c);
  				atcmds = lappend(atcmds, atcmd);
  			}
  		}
  
  		/*
+ 		 * If existing view has any optional parameters, we shall reset
+ 		 * them at first using ALTER TABLE RESET OPTION internally.
+ 		 */
+ 		tuple = SearchSysCache1(RELOID, DatumGetObjectId(viewOid));
+ 		if (!HeapTupleIsValid(tuple))
+ 			elog(ERROR, "cache lookup failed for relation %u", viewOid);
+ 		datum = SysCacheGetAttr(RELOID,
+ 								tuple,
+ 								Anum_pg_class_reloptions,
+ 								&isnull);
+ 		if (!isnull)
+ 		{
+ 			List	   *cur_opts = untransformRelOptions(datum);
+ 			ListCell   *cell;
+ 
+ 			atcmd = makeNode(AlterTableCmd);
+ 			atcmd->subtype = AT_ResetRelOptions;
+ 			/* Make sure arguments being NULL */
+ 			foreach (cell, cur_opts)
+ 				((DefElem *) lfirst(cell))->arg = NULL;
+ 			atcmd->def = (Node *)cur_opts;
+ 			atcmds = lappend(atcmds, atcmd);
+ 		}
+ 		ReleaseSysCache(tuple);
+ 
+ 		/*
+ 		 * If optional parameters are specified, we must set options
+ 		 * using ALTER TABLE SET OPTION internally.
+ 		 */
+ 		if (list_length(options) > 0)
+ 		{
+ 			atcmd = makeNode(AlterTableCmd);
+ 			atcmd->subtype = AT_SetRelOptions;
+ 			atcmd->def = (Node *)options;
+ 			atcmds = lappend(atcmds, atcmd);
+ 		}
+ 
+ 		if (atcmds != NIL)
+ 			AlterTableInternal(viewOid, atcmds, true);
+ 
+ 		/*
  		 * Seems okay, so return the OID of the pre-existing view.
  		 */
  		relation_close(rel, NoLock);	/* keep the lock! */
***************
*** 255,260 **** DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
--- 299,307 ----
  		createStmt->tablespacename = NULL;
  		createStmt->if_not_exists = false;
  
+ 		if (options != NIL)
+ 			createStmt->options = list_concat(createStmt->options, options);
+ 
  		/*
  		 * 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"
***************
*** 513,519 **** DefineView(ViewStmt *stmt, const char *queryString)
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 									stmt->replace, namespaceId);
  
  	/*
  	 * The relation we have just created is not visible to any other commands
--- 560,567 ----
  	 * aborted.
  	 */
  	viewOid = DefineVirtualRelation(view, viewParse->targetList,
! 									stmt->replace, namespaceId,
! 									stmt->options);
  
  	/*
  	 * 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
***************
*** 1944,1949 **** _copyRangeTblEntry(RangeTblEntry *from)
--- 1944,1950 ----
  	COPY_SCALAR_FIELD(relid);
  	COPY_SCALAR_FIELD(relkind);
  	COPY_NODE_FIELD(subquery);
+ 	COPY_SCALAR_FIELD(security_barrier);
  	COPY_SCALAR_FIELD(jointype);
  	COPY_NODE_FIELD(joinaliasvars);
  	COPY_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 2303,2308 **** _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
--- 2303,2309 ----
  	COMPARE_SCALAR_FIELD(relid);
  	COMPARE_SCALAR_FIELD(relkind);
  	COMPARE_NODE_FIELD(subquery);
+ 	COMPARE_SCALAR_FIELD(security_barrier);
  	COMPARE_SCALAR_FIELD(jointype);
  	COMPARE_NODE_FIELD(joinaliasvars);
  	COMPARE_NODE_FIELD(funcexpr);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 2311,2316 **** _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
--- 2311,2317 ----
  			break;
  		case RTE_SUBQUERY:
  			WRITE_NODE_FIELD(subquery);
+ 			WRITE_BOOL_FIELD(security_barrier);
  			break;
  		case RTE_JOIN:
  			WRITE_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1191,1196 **** _readRangeTblEntry(void)
--- 1191,1197 ----
  			break;
  		case RTE_SUBQUERY:
  			READ_NODE_FIELD(subquery);
+ 			READ_BOOL_FIELD(security_barrier);
  			break;
  		case RTE_JOIN:
  			READ_ENUM_FIELD(jointype, JoinType);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 7266,7291 **** transaction_mode_list_or_empty:
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
  					n->view = $4;
  					n->view->relpersistence = $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->relpersistence = $4;
  					n->aliases = $7;
! 					n->query = $9;
  					n->replace = true;
  					$$ = (Node *) n;
  				}
  		;
--- 7266,7293 ----
   *
   *****************************************************************************/
  
! ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
  					n->view = $4;
  					n->view->relpersistence = $2;
  					n->aliases = $5;
! 					n->query = $8;
  					n->replace = false;
+ 					n->options = $6;
  					$$ = (Node *) n;
  				}
! 		| CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list opt_reloptions
  				AS SelectStmt opt_check_option
  				{
  					ViewStmt *n = makeNode(ViewStmt);
  					n->view = $6;
  					n->view->relpersistence = $4;
  					n->aliases = $7;
! 					n->query = $10;
  					n->replace = true;
+ 					n->options = $8;
  					$$ = (Node *) n;
  				}
  		;
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 1383,1388 **** ApplyRetrieveRule(Query *parsetree,
--- 1383,1389 ----
  	rte->rtekind = RTE_SUBQUERY;
  	rte->relid = InvalidOid;
  	rte->subquery = rule_action;
+ 	rte->security_barrier = RelationIsSecurityView(relation);
  	rte->inh = false;			/* must not be set for a subquery */
  
  	/*
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 377,382 **** RelationParseRelOptions(Relation relation, HeapTuple tuple)
--- 377,383 ----
  		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
***************
*** 12023,12030 **** dumpTableSchema(Archive *fout, TableInfo *tbinfo)
  		if (binary_upgrade)
  			binary_upgrade_set_pg_class_oids(q, tbinfo->dobj.catId.oid, false);
  
! 		appendPQExpBuffer(q, "CREATE VIEW %s AS\n    %s\n",
! 						  fmtId(tbinfo->dobj.name), viewdef);
  
  		appendPQExpBuffer(labelq, "VIEW %s",
  						  fmtId(tbinfo->dobj.name));
--- 12023,12032 ----
  		if (binary_upgrade)
  			binary_upgrade_set_pg_class_oids(q, tbinfo->dobj.catId.oid, false);
  
! 		appendPQExpBuffer(q, "CREATE VIEW %s", fmtId(tbinfo->dobj.name));
! 		if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0)
! 			appendPQExpBuffer(q, " WITH (%s)", tbinfo->reloptions);
! 		appendPQExpBuffer(q, " AS\n    %s\n", viewdef);
  
  		appendPQExpBuffer(labelq, "VIEW %s",
  						  fmtId(tbinfo->dobj.name));
*** 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
***************
*** 706,711 **** typedef struct RangeTblEntry
--- 706,713 ----
  	 */
  	Query	   *subquery;		/* the sub-query */
  
+ 	bool		security_barrier;	/* Was a security barrier view? */
+ 
  	/*
  	 * Fields valid for a join RTE (else NULL/zero):
  	 *
***************
*** 2339,2344 **** typedef struct ViewStmt
--- 2341,2347 ----
  	List	   *aliases;		/* target column names */
  	Node	   *query;			/* the SELECT query */
  	bool		replace;		/* replace an existing view? */
+ 	List	   *options;		/* options from WITH clause */
  } ViewStmt;
  
  /* ----------------------
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
***************
*** 168,174 **** typedef struct RelationData
  
  /*
   * StdRdOptions
!  *		Standard contents of rd_options for heaps and generic indexes.
   *
   * RelationGetFillFactor() and RelationGetTargetPageFreeSpace() can only
   * be applied to relations that use this format or a superset for
--- 168,174 ----
  
  /*
   * StdRdOptions
!  *		Standard contents of rd_options for heaps, views and generic indexes.
   *
   * RelationGetFillFactor() and RelationGetTargetPageFreeSpace() can only
   * be applied to relations that use this format or a superset for
***************
*** 194,199 **** typedef struct StdRdOptions
--- 194,200 ----
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
  	int			fillfactor;		/* page fill factor in percent (0..100) */
  	AutoVacOpts autovacuum;		/* autovacuum-related options */
+ 	bool		security_barrier;	/* performs as security-barrier view */
  } StdRdOptions;
  
  #define HEAP_MIN_FILLFACTOR			10
***************
*** 222,227 **** typedef struct StdRdOptions
--- 223,236 ----
  	(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
  
  /*
+  * RelationIsSecurityView
+  *             Returns whether the relation is security view, or not
+  */
+ #define RelationIsSecurityView(relation)	\
+ 	((relation)->rd_options ?				\
+ 	 ((StdRdOptions *) (relation)->rd_options)->security_barrier : false)
+ 
+ /*
   * RelationIsValid
   *		True iff relation descriptor is valid.
   */
*** a/src/test/regress/expected/create_view.out
--- b/src/test/regress/expected/create_view.out
***************
*** 239,244 **** And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%')
--- 239,282 ----
       1
  (1 row)
  
+ --Should work correctly to leaky-view scenario
+ CREATE TABLE lvtest1 (a int, b text);
+ CREATE TABLE lvtest2 (x int, y text);
+ INSERT INTO lvtest1 VALUES (10, 'aaa'), (11, 'bbb'), (12, 'ccc'), (13, 'ddd');
+ INSERT INTO lvtest2 VALUES (11, 'xxx'), (12, 'yyy'), (13, 'zzz'), (14, 'xyz');
+ CREATE OR REPLACE VIEW leaky_v1 WITH (security_barrier=true) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 0;	-- OK
+ CREATE OR REPLACE VIEW leaky_v2 WITH (security_barrier=false) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;	-- OK
+ CREATE OR REPLACE VIEW leaky_v3 WITH (invalid_option=1234) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;	-- Fail
+ ERROR:  unrecognized parameter "invalid_option"
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+  relname  |        reloptions        
+ ----------+--------------------------
+  leaky_v1 | {security_barrier=true}
+  leaky_v2 | {security_barrier=false}
+ (2 rows)
+ 
+ ALTER TABLE leaky_v1 RESET ( security_barrier );
+ ALTER TABLE leaky_v2 SET ( security_barrier );
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+  relname  |       reloptions        
+ ----------+-------------------------
+  leaky_v1 | 
+  leaky_v2 | {security_barrier=true}
+ (2 rows)
+ 
+ -- reloptions should be reset
+ CREATE OR REPLACE VIEW leaky_v2 AS
+ 	    SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+  relname  | reloptions 
+ ----------+------------
+  leaky_v1 | 
+  leaky_v2 | 
+ (2 rows)
+ 
  DROP SCHEMA temp_view_test CASCADE;
  NOTICE:  drop cascades to 22 other objects
  DETAIL:  drop cascades to table temp_view_test.base_table
***************
*** 264,270 **** drop cascades to view temp_view_test.v8
  drop cascades to sequence temp_view_test.seq1
  drop cascades to view temp_view_test.v9
  DROP SCHEMA testviewschm2 CASCADE;
! NOTICE:  drop cascades to 16 other objects
  DETAIL:  drop cascades to table t1
  drop cascades to view temporal1
  drop cascades to view temporal2
--- 302,308 ----
  drop cascades to sequence temp_view_test.seq1
  drop cascades to view temp_view_test.v9
  DROP SCHEMA testviewschm2 CASCADE;
! NOTICE:  drop cascades to 20 other objects
  DETAIL:  drop cascades to table t1
  drop cascades to view temporal1
  drop cascades to view temporal2
***************
*** 281,284 **** drop cascades to table tbl3
--- 319,326 ----
  drop cascades to table tbl4
  drop cascades to view mytempview
  drop cascades to view pubview
+ drop cascades to table lvtest1
+ drop cascades to table lvtest2
+ drop cascades to view leaky_v1
+ drop cascades to view leaky_v2
  SET search_path to public;
*** a/src/test/regress/sql/create_view.sql
--- b/src/test/regress/sql/create_view.sql
***************
*** 191,196 **** AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j);
--- 191,220 ----
  SELECT count(*) FROM pg_class where relname LIKE 'mytempview'
  And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%');
  
+ --Should work correctly to leaky-view scenario
+ CREATE TABLE lvtest1 (a int, b text);
+ CREATE TABLE lvtest2 (x int, y text);
+ 
+ INSERT INTO lvtest1 VALUES (10, 'aaa'), (11, 'bbb'), (12, 'ccc'), (13, 'ddd');
+ INSERT INTO lvtest2 VALUES (11, 'xxx'), (12, 'yyy'), (13, 'zzz'), (14, 'xyz');
+ 
+ CREATE OR REPLACE VIEW leaky_v1 WITH (security_barrier=true) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 0;	-- OK
+ CREATE OR REPLACE VIEW leaky_v2 WITH (security_barrier=false) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;	-- OK
+ CREATE OR REPLACE VIEW leaky_v3 WITH (invalid_option=1234) AS
+ 	   SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;	-- Fail
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+ 
+ ALTER TABLE leaky_v1 RESET ( security_barrier );
+ ALTER TABLE leaky_v2 SET ( security_barrier );
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+ 
+ -- reloptions should be reset
+ CREATE OR REPLACE VIEW leaky_v2 AS
+ 	    SELECT * FROM lvtest1 JOIN lvtest2 ON a = x WHERE a % 2 = 1;
+ SELECT relname, reloptions FROM pg_class WHERE relname like 'leaky_v%';
+ 
  DROP SCHEMA temp_view_test CASCADE;
  DROP SCHEMA testviewschm2 CASCADE;
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to