Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 15cb32d

Browse files
committed
This is the final state of the rule system for 6.4 after the
patch is applied: Rewrite rules on relation level work fine now. Event qualifications on insert/update/delete rules work fine now. I added the new keyword OLD to reference the CURRENT tuple. CURRENT will be removed in 6.5. Update rules can reference NEW and OLD in the rule qualification and the actions. Insert/update/delete rules on views can be established to let them behave like real tables. For insert/update/delete rules multiple actions are supported now. The actions can also be surrounded by parantheses to make psql happy. Multiple actions are required if update to a view requires updates to multiple tables. Regular users are permitted to create/drop rules on tables they have RULE permissions for (DefineQueryRewrite() is now able to get around the access restrictions on pg_rewrite). This enables view creation for regular users too. This required an extra boolean parameter to pg_parse_and_plan() that tells to set skipAcl on all rangetable entries of the resulting queries. There is a new function pg_exec_query_acl_override() that could be used by backend utilities to use this facility. All rule actions (not only views) inherit the permissions of the event relations owner. Sample: User A creates tables T1 and T2, creates rules that log INSERT/UPDATE/DELETE on T1 in T2 (like in the regression tests for rules I created) and grants ALL but RULE on T1 to user B. User B can now fully access T1 and the logging happens in T2. But user B cannot access T2 at all, only the rule actions can. And due to missing RULE permissions on T1, user B cannot disable logging. Rules on the attribute level are disabled (they don't work properly and since regular users are now permitted to create rules I decided to disable them). Rules on select must have exactly one action that is a select (so select rules must be a view definition). UPDATE NEW/OLD rules are disabled (still broken, but triggers can do it). There are two new system views (pg_rule and pg_view) that show the definition of the rules or views so the db admin can see what the users do. They use two new functions pg_get_ruledef() and pg_get_viewdef() that are builtins. The functions pg_get_ruledef() and pg_get_viewdef() could be used to implement rule and view support in pg_dump. PostgreSQL is now the only database system I know, that has rewrite rules on the query level. All others (where I found a rule statement at all) use stored database procedures or the like (triggers as we call them) for active rules (as some call them). Future of the rule system: The now disabled parts of the rule system (attribute level, multiple actions on select and update new stuff) require a complete new rewrite handler from scratch. The old one is too badly wired up. After 6.4 I'll start to work on a new rewrite handler, that fully supports the attribute level rules, multiple actions on select and update new. This will be available for 6.5 so we get full rewrite rule capabilities. Jan
1 parent f92994b commit 15cb32d

File tree

17 files changed

+1719
-125
lines changed

17 files changed

+1719
-125
lines changed

src/backend/catalog/heap.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.59 1998/08/20 22:07:32 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.60 1998/08/24 01:37:46 momjian Exp $
1111
*
1212
* INTERFACE ROUTINES
1313
* heap_create() - Create an uncataloged heap relation
@@ -1448,7 +1448,7 @@ start:;
14481448
sprintf(str, "select %s%s from %.*s", attrdef->adsrc, cast,
14491449
NAMEDATALEN, rel->rd_rel->relname.data);
14501450
setheapoverride(true);
1451-
planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None);
1451+
planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None, FALSE);
14521452
setheapoverride(false);
14531453
query = (Query *) (queryTree_list->qtrees[0]);
14541454

@@ -1519,7 +1519,7 @@ StoreRelCheck(Relation rel, ConstrCheck *check)
15191519
sprintf(str, "select 1 from %.*s where %s",
15201520
NAMEDATALEN, rel->rd_rel->relname.data, check->ccsrc);
15211521
setheapoverride(true);
1522-
planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None);
1522+
planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None, FALSE);
15231523
setheapoverride(false);
15241524
query = (Query *) (queryTree_list->qtrees[0]);
15251525

src/backend/catalog/pg_proc.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.19 1998/08/19 02:01:37 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.20 1998/08/24 01:37:47 momjian Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -214,7 +214,7 @@ ProcedureCreate(char *procedureName,
214214
if (strcmp(languageName, "sql") == 0)
215215
{
216216
plan_list = pg_parse_and_plan(prosrc, typev, parameterCount,
217-
&querytree_list, dest);
217+
&querytree_list, dest, FALSE);
218218

219219
/* typecheck return value */
220220
pg_checkretval(typeObjectId, querytree_list);

src/backend/executor/functions.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.17 1998/06/15 19:28:20 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.18 1998/08/24 01:37:48 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -113,7 +113,7 @@ init_execution_state(FunctionCachePtr fcache,
113113

114114

115115
planTree_list = (List *)
116-
pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
116+
pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None, FALSE);
117117

118118
for (i = 0; i < queryTree_list->len; i++)
119119
{

src/backend/executor/spi.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
642642
argtypes = plan->argtypes;
643643
}
644644
ptlist = planTree_list = (List *)
645-
pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None);
645+
pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None, FALSE);
646646

647647
_SPI_current->qtlist = queryTree_list;
648648

src/backend/libpq/be-pqexec.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.17 1998/06/15 19:28:25 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.18 1998/08/24 01:37:52 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -137,7 +137,7 @@ PQexec(char *query)
137137
* end up on the top of the portal stack.
138138
* ----------------
139139
*/
140-
pg_exec_query_dest(query, Local);
140+
pg_exec_query_dest(query, Local, FALSE);
141141

142142
/* ----------------
143143
* pop the portal off the portal stack and return the

src/backend/optimizer/path/xfunc.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.19 1998/08/19 02:02:13 momjian Exp $
12+
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.20 1998/08/24 01:37:53 momjian Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -528,7 +528,7 @@ xfunc_func_expense(LispValue node, LispValue args)
528528
if (nargs > 0)
529529
argOidVect = proc->proargtypes;
530530
planlist = (List) pg_parse_and_plan(pq_src, argOidVect, nargs,
531-
&parseTree_list, None);
531+
&parseTree_list, None, FALSE);
532532
if (IsA(node, Func))
533533
set_func_planlist((Func) node, planlist);
534534

src/backend/parser/keywords.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.38 1998/07/24 03:31:24 scrappy Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.39 1998/08/24 01:37:55 momjian Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -66,7 +66,12 @@ static ScanKeyword ScanKeywords[] = {
6666
{"createdb", CREATEDB},
6767
{"createuser", CREATEUSER},
6868
{"cross", CROSS},
69-
{"current", CURRENT},
69+
{"current", CURRENT}, /*
70+
* 6.4 to 6.5 is migration time!
71+
* CURRENT will be removed in 6.5!
72+
* Use OLD keyword in rules.
73+
* Jan
74+
*/
7075
{"current_date", CURRENT_DATE},
7176
{"current_time", CURRENT_TIME},
7277
{"current_timestamp", CURRENT_TIMESTAMP},
@@ -152,6 +157,7 @@ static ScanKeyword ScanKeywords[] = {
152157
{"null", NULL_P},
153158
{"numeric", NUMERIC},
154159
{"oids", OIDS},
160+
{"old", OLD},
155161
{"on", ON},
156162
{"operator", OPERATOR},
157163
{"option", OPTION},

src/backend/rewrite/locks.c

+102-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*
77
*
88
* IDENTIFICATION
9-
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.10 1998/06/15 19:29:06 momjian Exp $
9+
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.11 1998/08/24 01:37:56 momjian Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -18,6 +18,14 @@
1818
#include "utils/syscache.h" /* for SearchSysCache */
1919
#include "rewrite/locks.h" /* for rewrite specific lock defns */
2020

21+
#include "access/heapam.h" /* for ACL checking */
22+
#include "utils/syscache.h"
23+
#include "utils/acl.h"
24+
#include "utils/builtins.h"
25+
#include "catalog/pg_shadow.h"
26+
27+
static void checkLockPerms(List *locks, Query *parsetree, int rt_index);
28+
2129
/*
2230
* ThisLockWasTriggered
2331
*
@@ -156,5 +164,98 @@ matchLocks(CmdType event,
156164
}
157165
}
158166

167+
checkLockPerms(real_locks, parsetree, varno);
168+
159169
return (real_locks);
160170
}
171+
172+
173+
static void
174+
checkLockPerms(List *locks, Query *parsetree, int rt_index)
175+
{
176+
Relation ev_rel;
177+
HeapTuple usertup;
178+
char *evowner;
179+
RangeTblEntry *rte;
180+
int32 reqperm;
181+
int32 aclcheck_res;
182+
int i;
183+
List *l;
184+
185+
if (locks == NIL)
186+
return;
187+
188+
/*
189+
* Get the usename of the rules event relation owner
190+
*/
191+
rte = (RangeTblEntry *)nth(rt_index - 1, parsetree->rtable);
192+
ev_rel = heap_openr(rte->relname);
193+
usertup = SearchSysCacheTuple(USESYSID,
194+
ObjectIdGetDatum(ev_rel->rd_rel->relowner),
195+
0, 0, 0);
196+
if (!HeapTupleIsValid(usertup))
197+
{
198+
elog(ERROR, "cache lookup for userid %d failed",
199+
ev_rel->rd_rel->relowner);
200+
}
201+
heap_close(ev_rel);
202+
evowner = nameout(&(((Form_pg_shadow) GETSTRUCT(usertup))->usename));
203+
204+
/*
205+
* Check all the locks, that should get fired on this query
206+
*/
207+
foreach (l, locks) {
208+
RewriteRule *onelock = (RewriteRule *)lfirst(l);
209+
List *action;
210+
211+
/*
212+
* In each lock check every action
213+
*/
214+
foreach (action, onelock->actions) {
215+
Query *query = (Query *)lfirst(action);
216+
217+
/*
218+
* In each action check every rangetable entry
219+
* for read/write permission of the event relations
220+
* owner depending on if it's the result relation
221+
* (write) or not (read)
222+
*/
223+
for (i = 2; i < length(query->rtable); i++) {
224+
if (i + 1 == query->resultRelation)
225+
switch (query->resultRelation) {
226+
case CMD_INSERT:
227+
reqperm = ACL_AP;
228+
break;
229+
default:
230+
reqperm = ACL_WR;
231+
break;
232+
}
233+
else
234+
reqperm = ACL_RD;
235+
236+
rte = (RangeTblEntry *)nth(i, query->rtable);
237+
aclcheck_res = pg_aclcheck(rte->relname,
238+
evowner, reqperm);
239+
if (aclcheck_res != ACLCHECK_OK) {
240+
elog(ERROR, "%s: %s",
241+
rte->relname,
242+
aclcheck_error_strings[aclcheck_res]);
243+
}
244+
245+
/*
246+
* So this is allowed due to the permissions
247+
* of the rules event relation owner. But
248+
* let's see if the next one too
249+
*/
250+
rte->skipAcl = TRUE;
251+
}
252+
}
253+
}
254+
255+
/*
256+
* Phew, that was close
257+
*/
258+
return;
259+
}
260+
261+

src/backend/rewrite/rewriteDefine.c

+52-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.18 1998/08/19 02:02:29 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.19 1998/08/24 01:37:58 momjian Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -131,7 +131,7 @@ InsertRule(char *rulname,
131131
rulname, evtype, eventrel_oid, evslot_index, actionbuf,
132132
qualbuf, is_instead);
133133

134-
pg_exec_query(rulebuf);
134+
pg_exec_query_acl_override(rulebuf);
135135

136136
return (LastOidProcessed);
137137
}
@@ -192,12 +192,61 @@ DefineQueryRewrite(RuleStmt *stmt)
192192
Oid event_attype = 0;
193193
char *actionP,
194194
*event_qualP;
195-
195+
List *l;
196+
Query *query;
197+
198+
/* ----------
199+
* The current rewrite handler is known to work on relation level
200+
* rules only. And for SELECT events, it expects one non-nothing
201+
* action that is instead. Since we now hand out views and rules
202+
* to regular users, we must deny anything else.
203+
*
204+
* I know that I must write a new rewrite handler from scratch
205+
* for 6.5 so we can remove these checks and allow all the rules.
206+
*
207+
* Jan
208+
* ----------
209+
*/
196210
if (event_obj->attrs)
211+
elog(ERROR, "attribute level rules currently not supported");
212+
/*
197213
eslot_string = strVal(lfirst(event_obj->attrs));
214+
*/
198215
else
199216
eslot_string = NULL;
200217

218+
if (action != NIL)
219+
foreach (l, action) {
220+
query = (Query *)lfirst(l);
221+
if (query->resultRelation == 1) {
222+
elog(NOTICE, "rule actions on OLD currently not supported");
223+
elog(ERROR, " use views or triggers instead");
224+
}
225+
if (query->resultRelation == 2) {
226+
elog(NOTICE, "rule actions on NEW currently not supported");
227+
elog(ERROR, " use triggers instead");
228+
}
229+
}
230+
231+
if (event_type == CMD_SELECT) {
232+
if (length(action) == 0) {
233+
elog(NOTICE, "instead nothing rules on select currently not supported");
234+
elog(ERROR, " use views instead");
235+
}
236+
if (length(action) > 1) {
237+
elog(ERROR, "multiple action rules on select currently not supported");
238+
}
239+
query = (Query *)lfirst(action);
240+
if (!is_instead || query->commandType != CMD_SELECT) {
241+
elog(ERROR, "only instead-select rules currently supported on select");
242+
}
243+
}
244+
/*
245+
* This rule is currently allowed - too restricted I know -
246+
* but women and children first
247+
* Jan
248+
*/
249+
201250
event_relation = heap_openr(event_obj->relname);
202251
if (event_relation == NULL)
203252
elog(ERROR, "virtual relations not supported yet");

0 commit comments

Comments
 (0)