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