From: | Kohei KaiGai <kaigai(at)kaigai(dot)gr(dot)jp> |
---|---|
To: | Noah Misch <noah(at)2ndquadrant(dot)com> |
Cc: | Robert Haas <robertmhaas(at)gmail(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Kohei Kaigai <Kohei(dot)Kaigai(at)emea(dot)nec(dot)com>, Stephen Frost <sfrost(at)snowman(dot)net>, Heikki Linnakangas <heikki(dot)linnakangas(at)enterprisedb(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org> |
Subject: | Re: [v9.2] Fix leaky-view problem, part 1 |
Date: | 2011-07-06 20:25:12 |
Message-ID: | CADyhKSU-dLudYu53FWaJFRYkeazn0_vFKFdgqUVra=_pvqy4Vw@mail.gmail.com |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
Thanks for your detailed viewing and testing.
The attached patches are revised version.
Part-0)
* The patch was re-generated using context diff, instead of unified diff
* Documentation on ALTER VIEW was added
* Behavior of CREATE OR REPLACE VIEW was revised; when we replace
an existing view, reloption shall be reset, even if a particular
value was set.
* And, cosmetic changes; eliminate warnings due to lack of type cast.
Part-1)
* I removed the code to increment depth by 2, and preserve the latest bit,
because we have no module to utilize this mechanism right now.
Thanks,
2011/7/5 Noah Misch <noah(at)2ndquadrant(dot)com>:
> On Sun, Jul 03, 2011 at 11:33:38AM +0200, Kohei KaiGai wrote:
>> The attached patches are revised version.
>>
>> The part-0 provides 'security_barrier' option for view definition, and performs
>> as a common basis of part-1 and part-2 patches.
>> Syntax is extended as follows:
>>
>> CREATE VIEW view_name [WITH (param [=value])] AS query;
>>
>> We can also turn on/off this security_barrier setting by ALTER TABLE with
>> SET/RESET options.
>>
>> The part-1 patch enforces the qualifiers originally located under the security
>> barrier view to be launched prior to ones supplied on upper level.
>> The differences from the previous version is this barrier become conditional,
>> not always. So, existing optimization will be applied without any changes
>> onto non-security-barrier views.
>
> I tested various query trees I considered interesting, and this version had
> sound semantics for all of them. I have one suggestion for CREATE OR REPLACE
> VIEW semantics, plus various cosmetic comments.
>
> These patches are unified diffs, rather than project-standard context diffs.
>
> Part 0:
>> --- a/doc/src/sgml/ref/create_view.sgml
>> +++ b/doc/src/sgml/ref/create_view.sgml
>> @@ -22,6 +22,7 @@ PostgreSQL documentation
>> <refsynopsisdiv>
>> <synopsis>
>> CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
>> + [ WITH ( parameter [= value] [, ... ] ) ]
>
> This needs a bit more markup; see the corresponding case in create_table.sgml.
>
> alter_view.sgml also needs an update. Incidentally, we should use ALTER VIEW
> SET OPTION when referring to setting this for a view. ALTER TABLE SET OPTION
> will also support views, since that's the general pattern for tablecmds.c type
> checks, but that's largely an implementation detail.
>
>> --- a/src/backend/commands/view.c
>> +++ b/src/backend/commands/view.c
>> @@ -97,7 +97,8 @@ isViewOnTempTable_walker(Node *node, void *context)
>> *---------------------------------------------------------------------
>> */
>> static Oid
>> -DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>> +DefineVirtualRelation(const RangeVar *relation, List *tlist,
>> + bool replace, List *options)
>> {
>> Oid viewOid,
>> namespaceId;
>
> This hunk and the hunk for the function's caller get rejects due to another
> recent signature change.
>
>> @@ -167,6 +168,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>> {
>> Relation rel;
>> TupleDesc descriptor;
>> + List *atcmds = NIL;
>> + AlterTableCmd *atcmd;
>>
>> /*
>> * Yes. Get exclusive lock on the existing view ...
>> @@ -211,14 +214,11 @@ 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--;
>> @@ -229,10 +229,24 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
>> atcmd->def = (Node *) lfirst(c);
>> atcmds = lappend(atcmds, atcmd);
>> }
>> - AlterTableInternal(viewOid, atcmds, true);
>> }
>>
>> /*
>> + * If optional parameters are specified, we must set options
>> + * using ALTER TABLE SET OPTION internally.
>
> I think CREATE OR REPLACE VIEW should replace the option list, while ALTER
> VIEW SET OPTION should retain its current behavior. That is, this should
> leave the view with no options set:
>
> create or replace view v0(n) with (security_barrier) as values (1), (2), (3), (4);
> select reloptions from pg_class where oid = 'v0'::regclass;
> create or replace view v0(n) as values (4), (3), (2), (1);
> select reloptions from pg_class where oid = 'v0'::regclass;
>
>> + */
>> + if (list_length(options) > 0)
>> + {
>> + atcmd = makeNode(AlterTableCmd);
>> + atcmd->subtype = AT_SetRelOptions;
>> + atcmd->def = options;
>
> This line produces a warning:
>
> view.c: In function `DefineVirtualRelation':
> view.c:240: warning: assignment from incompatible pointer type
>
>> +
>> + 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! */
>
> Part 1:
>> --- a/src/backend/optimizer/plan/planner.c
>> +++ b/src/backend/optimizer/plan/planner.c
>
>> @@ -2993,6 +3001,131 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
>> }
>> }
>>
>> +/*
>> + * mark_qualifiers_depth
>> + *
>> + * It marks depth field of the each expression nodes that eventually
>> + * invokes functions, to track the original nest-level. On the evaluation
>> + * of qualifiers within WHERE or JOIN ... ON clauses during relation scans,
>> + * these items shall be reordered according to the nest-level and estimated
>> + * cost.
>> + * The optimizer may pull-up simple sub-queries or join clause, and
>> + * qualifiers to filter out tuples shall be mixed with ones in upper-
>> + * level. Thus, we need to track the original nest-level of qualifiers
>> + * to prevent reverse of order in evaluation, because some of qualifiers
>> + * can have side-effects that allows to leak supplied argument to outside.
>> + * It can be abused to break row-level security using a user defined function
>> + * with very small estimated cost, so nest level of qualifiers originated
>> + * from is used as a criteria, rather than estimated cost, to decide order
>> + * to evaluate qualifiers.
>> + */
>> +static bool
>> +mark_qualifiers_depth_walker(Node *node, void *context)
>> +{
>> + int depth = *((int *)(context));
>> +
>> + if (node == NULL)
>> + return false;
>> + if (IsA(node, FuncExpr))
>> + {
>> + FuncExpr *exp = (FuncExpr *)node;
>> +
>> + exp->depth = depth | (exp->depth & 1);
>
> Why did these change from plain "exp->depth = depth;" of the last version?
> Since no core code sets a 1-bit in a depth value, I assume it must be related
> to your future-use design for that bit. If so: could an external module
> realistically take advantage of this? If yes, then a mere comment is in
> order. If not, I think we should remove this (and the incrementing by 2) and
> add it again in the future patch that makes use thereof.
>
>> --- a/src/test/regress/sql/select_views.sql
>> +++ b/src/test/regress/sql/select_views.sql
>> @@ -8,3 +8,42 @@ SELECT * FROM street;
>> SELECT name, #thepath FROM iexit ORDER BY 1, 2;
>>
>> SELECT * FROM toyemp WHERE name = 'sharon';
>> +
>> +--
>> +-- Test for leaky-view
>> +--
>> +
>> +CREATE USER alice;
>> +CREATE FUNCTION f_leak(text, text)
>> + RETURNS bool LANGUAGE 'plpgsql'
>> + COST 0.00000001
>> + AS 'begin raise notice ''% => %'', $1, $2; return true; end';
>> +CREATE TABLE credit_cards (
>> + name text,
>> + number text,
>> + expired text
>> +);
>> +INSERT INTO credit_cards VALUES ('alice', '1111-2222-3333-4444', 'Aug-2012'),
>> + ('bob', '5555-6666-7777-8888', 'Nov-2016'),
>> + ('eve', '9801-2345-6789-0123', 'Jan-2018');
>> +CREATE VIEW your_credit_normal AS
>> + SELECT * FROM credit_cards WHERE name = getpgusername();
>> +CREATE VIEW your_credit_secure WITH (security_barrier) AS
>> + SELECT * FROM credit_cards WHERE name = getpgusername();
>> +
>> +GRANT SELECT ON your_credit_normal TO public;
>> +GRANT SELECT ON your_credit_secure TO public;
>> +-- run leaky view
>> +SET SESSION AUTHORIZATION alice;
>> +
>> +SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
>> +EXPLAIN (COSTS OFF) SELECT * FROM your_credit_normal WHERE f_leak(number,expired);
>> +
>> +SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
>> +EXPLAIN (COSTS OFF) SELECT * FROM your_credit_secure WHERE f_leak(number,expired);
>> +
>> +\c -
>> +-- cleanups
>> +DROP ROLE IF EXISTS alice;
>> +DROP FUNCTION IF EXISTS f_leak(text);
>> +DROP TABLE IF EXISTS credit_cards CASCADE;
>
> Keep the view around. That way, pg_dump tests of the regression database will
> test the dumping of this view option. (Your pg_dump support for this feature
> does work fine, though.)
>
> Thanks,
> nm
>
--
KaiGai Kohei <kaigai(at)kaigai(dot)gr(dot)jp>
Attachment | Content-Type | Size |
---|---|---|
pgsql-v9.2-fix-leaky-view-part-0.v4.patch | application/octet-stream | 21.6 KB |
pgsql-v9.2-fix-leaky-view-part-1.v4.patch | application/octet-stream | 33.8 KB |
From | Date | Subject | |
---|---|---|---|
Next Message | HarmeekSingh Bedi | 2011-07-06 20:29:03 | Expression Pruning in postgress |
Previous Message | Robert Haas | 2011-07-06 19:17:44 | Re: reducing the overhead of frequent table locks, v4 |