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 <[email protected]>:
> On Thu, Jul 07, 2011 at 03:56:26PM +0100, Kohei KaiGai wrote:
>> 2011/7/7 Noah Misch <[email protected]>:
>> > 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 <[email protected]>
*** 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers