1 /*-------------------------------------------------------------------------
4 * routines for defining a rewrite rule
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/dependency.h"
19 #include "catalog/indexing.h"
20 #include "catalog/namespace.h"
21 #include "catalog/pg_rewrite.h"
22 #include "catalog/storage.h"
23 #include "miscadmin.h"
24 #include "nodes/nodeFuncs.h"
25 #include "parser/parse_utilcmd.h"
26 #include "rewrite/rewriteDefine.h"
27 #include "rewrite/rewriteManip.h"
28 #include "rewrite/rewriteSupport.h"
29 #include "utils/acl.h"
30 #include "utils/builtins.h"
31 #include "utils/inval.h"
32 #include "utils/lsyscache.h"
33 #include "utils/rel.h"
34 #include "utils/syscache.h"
35 #include "utils/tqual.h"
38 static void checkRuleResultList(List
*targetList
, TupleDesc resultDesc
,
40 static bool setRuleCheckAsUser_walker(Node
*node
, Oid
*context
);
41 static void setRuleCheckAsUser_Query(Query
*qry
, Oid userid
);
46 * takes the arguments and inserts them as a row into the system
47 * relation "pg_rewrite"
50 InsertRule(char *rulname
,
53 AttrNumber evslot_index
,
59 char *evqual
= nodeToString(event_qual
);
60 char *actiontree
= nodeToString((Node
*) action
);
62 Datum values
[Natts_pg_rewrite
];
63 bool nulls
[Natts_pg_rewrite
];
64 bool replaces
[Natts_pg_rewrite
];
66 Relation pg_rewrite_desc
;
72 bool is_update
= false;
75 * Set up *nulls and *values arrays
77 MemSet(nulls
, false, sizeof(nulls
));
80 namestrcpy(&rname
, rulname
);
81 values
[i
++] = NameGetDatum(&rname
); /* rulename */
82 values
[i
++] = ObjectIdGetDatum(eventrel_oid
); /* ev_class */
83 values
[i
++] = Int16GetDatum(evslot_index
); /* ev_attr */
84 values
[i
++] = CharGetDatum(evtype
+ '0'); /* ev_type */
85 values
[i
++] = CharGetDatum(RULE_FIRES_ON_ORIGIN
); /* ev_enabled */
86 values
[i
++] = BoolGetDatum(evinstead
); /* is_instead */
87 values
[i
++] = CStringGetTextDatum(evqual
); /* ev_qual */
88 values
[i
++] = CStringGetTextDatum(actiontree
); /* ev_action */
91 * Ready to store new pg_rewrite tuple
93 pg_rewrite_desc
= heap_open(RewriteRelationId
, RowExclusiveLock
);
96 * Check to see if we are replacing an existing tuple
98 oldtup
= SearchSysCache(RULERELNAME
,
99 ObjectIdGetDatum(eventrel_oid
),
100 PointerGetDatum(rulname
),
103 if (HeapTupleIsValid(oldtup
))
107 (errcode(ERRCODE_DUPLICATE_OBJECT
),
108 errmsg("rule \"%s\" for relation \"%s\" already exists",
109 rulname
, get_rel_name(eventrel_oid
))));
112 * When replacing, we don't need to replace every attribute
114 MemSet(replaces
, false, sizeof(replaces
));
115 replaces
[Anum_pg_rewrite_ev_attr
- 1] = true;
116 replaces
[Anum_pg_rewrite_ev_type
- 1] = true;
117 replaces
[Anum_pg_rewrite_is_instead
- 1] = true;
118 replaces
[Anum_pg_rewrite_ev_qual
- 1] = true;
119 replaces
[Anum_pg_rewrite_ev_action
- 1] = true;
121 tup
= heap_modify_tuple(oldtup
, RelationGetDescr(pg_rewrite_desc
),
122 values
, nulls
, replaces
);
124 simple_heap_update(pg_rewrite_desc
, &tup
->t_self
, tup
);
126 ReleaseSysCache(oldtup
);
128 rewriteObjectId
= HeapTupleGetOid(tup
);
133 tup
= heap_form_tuple(pg_rewrite_desc
->rd_att
, values
, nulls
);
135 rewriteObjectId
= simple_heap_insert(pg_rewrite_desc
, tup
);
138 /* Need to update indexes in either case */
139 CatalogUpdateIndexes(pg_rewrite_desc
, tup
);
143 /* If replacing, get rid of old dependencies and make new ones */
145 deleteDependencyRecordsFor(RewriteRelationId
, rewriteObjectId
);
148 * Install dependency on rule's relation to ensure it will go away on
149 * relation deletion. If the rule is ON SELECT, make the dependency
150 * implicit --- this prevents deleting a view's SELECT rule. Other kinds
151 * of rules can be AUTO.
153 myself
.classId
= RewriteRelationId
;
154 myself
.objectId
= rewriteObjectId
;
155 myself
.objectSubId
= 0;
157 referenced
.classId
= RelationRelationId
;
158 referenced
.objectId
= eventrel_oid
;
159 referenced
.objectSubId
= 0;
161 recordDependencyOn(&myself
, &referenced
,
162 (evtype
== CMD_SELECT
) ? DEPENDENCY_INTERNAL
: DEPENDENCY_AUTO
);
165 * Also install dependencies on objects referenced in action and qual.
167 recordDependencyOnExpr(&myself
, (Node
*) action
, NIL
,
170 if (event_qual
!= NULL
)
172 /* Find query containing OLD/NEW rtable entries */
173 Query
*qry
= (Query
*) linitial(action
);
175 qry
= getInsertSelectQuery(qry
, NULL
);
176 recordDependencyOnExpr(&myself
, event_qual
, qry
->rtable
,
180 heap_close(pg_rewrite_desc
, RowExclusiveLock
);
182 return rewriteObjectId
;
187 * Execute a CREATE RULE command.
190 DefineRule(RuleStmt
*stmt
, const char *queryString
)
196 /* Parse analysis ... */
197 transformRuleStmt(stmt
, queryString
, &actions
, &whereClause
);
199 /* ... find the relation ... */
200 relId
= RangeVarGetRelid(stmt
->relation
, false);
202 /* ... and execute */
203 DefineQueryRewrite(stmt
->rulename
,
217 * This is essentially the same as DefineRule() except that the rule's
218 * action and qual have already been passed through parse analysis.
221 DefineQueryRewrite(char *rulename
,
229 Relation event_relation
;
234 bool RelisBecomingView
= false;
237 * If we are installing an ON SELECT rule, we had better grab
238 * AccessExclusiveLock to ensure no SELECTs are currently running on the
239 * event relation. For other types of rules, it might be sufficient to
240 * grab ShareLock to lock out insert/update/delete actions. But for now,
241 * let's just grab AccessExclusiveLock all the time.
243 event_relation
= heap_open(event_relid
, AccessExclusiveLock
);
246 * Check user has permission to apply rules to this relation.
248 if (!pg_class_ownercheck(event_relid
, GetUserId()))
249 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
250 RelationGetRelationName(event_relation
));
253 * No rule actions that modify OLD or NEW
257 query
= (Query
*) lfirst(l
);
258 if (query
->resultRelation
== 0)
260 /* Don't be fooled by INSERT/SELECT */
261 if (query
!= getInsertSelectQuery(query
, NULL
))
263 if (query
->resultRelation
== PRS2_OLD_VARNO
)
265 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
266 errmsg("rule actions on OLD are not implemented"),
267 errhint("Use views or triggers instead.")));
268 if (query
->resultRelation
== PRS2_NEW_VARNO
)
270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
271 errmsg("rule actions on NEW are not implemented"),
272 errhint("Use triggers instead.")));
275 if (event_type
== CMD_SELECT
)
278 * Rules ON SELECT are restricted to view definitions
280 * So there cannot be INSTEAD NOTHING, ...
282 if (list_length(action
) == 0)
284 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
285 errmsg("INSTEAD NOTHING rules on SELECT are not implemented"),
286 errhint("Use views instead.")));
289 * ... there cannot be multiple actions, ...
291 if (list_length(action
) > 1)
293 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
294 errmsg("multiple actions for rules on SELECT are not implemented")));
297 * ... the one action must be a SELECT, ...
299 query
= (Query
*) linitial(action
);
301 query
->commandType
!= CMD_SELECT
||
302 query
->utilityStmt
!= NULL
||
303 query
->intoClause
!= NULL
)
305 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
306 errmsg("rules on SELECT must have action INSTEAD SELECT")));
309 * ... there can be no rule qual, ...
311 if (event_qual
!= NULL
)
313 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
314 errmsg("event qualifications are not implemented for rules on SELECT")));
317 * ... the targetlist of the SELECT action must exactly match the
318 * event relation, ...
320 checkRuleResultList(query
->targetList
,
321 RelationGetDescr(event_relation
),
325 * ... there must not be another ON SELECT rule already ...
327 if (!replace
&& event_relation
->rd_rules
!= NULL
)
331 for (i
= 0; i
< event_relation
->rd_rules
->numLocks
; i
++)
335 rule
= event_relation
->rd_rules
->rules
[i
];
336 if (rule
->event
== CMD_SELECT
)
338 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
339 errmsg("\"%s\" is already a view",
340 RelationGetRelationName(event_relation
))));
345 * ... and finally the rule must be named _RETURN.
347 if (strcmp(rulename
, ViewSelectRuleName
) != 0)
350 * In versions before 7.3, the expected name was _RETviewname. For
351 * backwards compatibility with old pg_dump output, accept that
352 * and silently change it to _RETURN. Since this is just a quick
353 * backwards-compatibility hack, limit the number of characters
354 * checked to a few less than NAMEDATALEN; this saves having to
355 * worry about where a multibyte character might have gotten
358 if (strncmp(rulename
, "_RET", 4) != 0 ||
359 strncmp(rulename
+ 4, RelationGetRelationName(event_relation
),
360 NAMEDATALEN
- 4 - 4) != 0)
362 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
363 errmsg("view rule for \"%s\" must be named \"%s\"",
364 RelationGetRelationName(event_relation
),
365 ViewSelectRuleName
)));
366 rulename
= pstrdup(ViewSelectRuleName
);
370 * Are we converting a relation to a view?
372 * If so, check that the relation is empty because the storage for the
373 * relation is going to be deleted. Also insist that the rel not have
374 * any triggers, indexes, or child tables. (Note: these tests are
375 * too strict, because they will reject relations that once had such
376 * but don't anymore. But we don't really care, because this whole
377 * business of converting relations to views is just a kluge to allow
378 * loading ancient pg_dump files.)
380 if (event_relation
->rd_rel
->relkind
!= RELKIND_VIEW
)
382 HeapScanDesc scanDesc
;
384 scanDesc
= heap_beginscan(event_relation
, SnapshotNow
, 0, NULL
);
385 if (heap_getnext(scanDesc
, ForwardScanDirection
) != NULL
)
387 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
388 errmsg("could not convert table \"%s\" to a view because it is not empty",
389 RelationGetRelationName(event_relation
))));
390 heap_endscan(scanDesc
);
392 if (event_relation
->rd_rel
->relhastriggers
)
394 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
395 errmsg("could not convert table \"%s\" to a view because it has triggers",
396 RelationGetRelationName(event_relation
)),
397 errhint("In particular, the table cannot be involved in any foreign key relationships.")));
399 if (event_relation
->rd_rel
->relhasindex
)
401 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
402 errmsg("could not convert table \"%s\" to a view because it has indexes",
403 RelationGetRelationName(event_relation
))));
405 if (event_relation
->rd_rel
->relhassubclass
)
407 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
408 errmsg("could not convert table \"%s\" to a view because it has child tables",
409 RelationGetRelationName(event_relation
))));
411 RelisBecomingView
= true;
417 * For non-SELECT rules, a RETURNING list can appear in at most one of
418 * the actions ... and there can't be any RETURNING list at all in a
419 * conditional or non-INSTEAD rule. (Actually, there can be at most
420 * one RETURNING list across all rules on the same event, but it seems
421 * best to enforce that at rule expansion time.) If there is a
422 * RETURNING list, it must match the event relation.
424 bool haveReturning
= false;
428 query
= (Query
*) lfirst(l
);
430 if (!query
->returningList
)
434 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
435 errmsg("cannot have multiple RETURNING lists in a rule")));
436 haveReturning
= true;
437 if (event_qual
!= NULL
)
439 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
440 errmsg("RETURNING lists are not supported in conditional rules")));
443 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
444 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
445 checkRuleResultList(query
->returningList
,
446 RelationGetDescr(event_relation
),
452 * This rule is allowed - prepare to install it.
456 /* discard rule if it's null action and not INSTEAD; it's a no-op */
457 if (action
!= NIL
|| is_instead
)
459 ruleId
= InsertRule(rulename
,
469 * Set pg_class 'relhasrules' field TRUE for event relation. If
470 * appropriate, also modify the 'relkind' field to show that the
471 * relation is now a view.
473 * Important side effect: an SI notice is broadcast to force all
474 * backends (including me!) to update relcache entries with the new
477 SetRelationRuleStatus(event_relid
, true, RelisBecomingView
);
481 * IF the relation is becoming a view, delete the storage files associated
482 * with it. NB: we had better have AccessExclusiveLock to do this ...
484 * XXX what about getting rid of its TOAST table? For now, we don't.
486 if (RelisBecomingView
)
487 RelationDropStorage(event_relation
);
489 /* Close rel, but keep lock till commit... */
490 heap_close(event_relation
, NoLock
);
494 * checkRuleResultList
495 * Verify that targetList produces output compatible with a tupledesc
497 * The targetList might be either a SELECT targetlist, or a RETURNING list;
498 * isSelect tells which. (This is mostly used for choosing error messages,
499 * but also we don't enforce column name matching for RETURNING.)
502 checkRuleResultList(List
*targetList
, TupleDesc resultDesc
, bool isSelect
)
508 foreach(tllist
, targetList
)
510 TargetEntry
*tle
= (TargetEntry
*) lfirst(tllist
);
512 Form_pg_attribute attr
;
515 /* resjunk entries may be ignored */
519 if (i
> resultDesc
->natts
)
521 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
523 errmsg("SELECT rule's target list has too many entries") :
524 errmsg("RETURNING list has too many entries")));
526 attr
= resultDesc
->attrs
[i
- 1];
527 attname
= NameStr(attr
->attname
);
530 * Disallow dropped columns in the relation. This won't happen in the
531 * cases we actually care about (namely creating a view via CREATE
532 * TABLE then CREATE RULE, or adding a RETURNING rule to a view).
533 * Trying to cope with it is much more trouble than it's worth,
534 * because we'd have to modify the rule to insert dummy NULLs at the
537 if (attr
->attisdropped
)
539 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
540 errmsg("cannot convert relation containing dropped columns to view")));
542 if (isSelect
&& strcmp(tle
->resname
, attname
) != 0)
544 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
545 errmsg("SELECT rule's target entry %d has different column name from \"%s\"", i
, attname
)));
547 if (attr
->atttypid
!= exprType((Node
*) tle
->expr
))
549 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
551 errmsg("SELECT rule's target entry %d has different type from column \"%s\"",
553 errmsg("RETURNING list's entry %d has different type from column \"%s\"",
557 * Allow typmods to be different only if one of them is -1, ie,
558 * "unspecified". This is necessary for cases like "numeric", where
559 * the table will have a filled-in default length but the select
560 * rule's expression will probably have typmod = -1.
562 tletypmod
= exprTypmod((Node
*) tle
->expr
);
563 if (attr
->atttypmod
!= tletypmod
&&
564 attr
->atttypmod
!= -1 && tletypmod
!= -1)
566 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
568 errmsg("SELECT rule's target entry %d has different size from column \"%s\"",
570 errmsg("RETURNING list's entry %d has different size from column \"%s\"",
574 if (i
!= resultDesc
->natts
)
576 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
578 errmsg("SELECT rule's target list has too few entries") :
579 errmsg("RETURNING list has too few entries")));
584 * Recursively scan a query or expression tree and set the checkAsUser
585 * field to the given userid in all rtable entries.
587 * Note: for a view (ON SELECT rule), the checkAsUser field of the *OLD*
588 * RTE entry will be overridden when the view rule is expanded, and the
589 * checkAsUser field of the *NEW* entry is irrelevant because that entry's
590 * requiredPerms bits will always be zero. However, for other types of rules
591 * it's important to set these fields to match the rule owner. So we just set
595 setRuleCheckAsUser(Node
*node
, Oid userid
)
597 (void) setRuleCheckAsUser_walker(node
, &userid
);
601 setRuleCheckAsUser_walker(Node
*node
, Oid
*context
)
605 if (IsA(node
, Query
))
607 setRuleCheckAsUser_Query((Query
*) node
, *context
);
610 return expression_tree_walker(node
, setRuleCheckAsUser_walker
,
615 setRuleCheckAsUser_Query(Query
*qry
, Oid userid
)
619 /* Set all the RTEs in this query node */
620 foreach(l
, qry
->rtable
)
622 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(l
);
624 if (rte
->rtekind
== RTE_SUBQUERY
)
626 /* Recurse into subquery in FROM */
627 setRuleCheckAsUser_Query(rte
->subquery
, userid
);
630 rte
->checkAsUser
= userid
;
633 /* Recurse into subquery-in-WITH */
634 foreach(l
, qry
->cteList
)
636 CommonTableExpr
*cte
= (CommonTableExpr
*) lfirst(l
);
638 setRuleCheckAsUser_Query((Query
*) cte
->ctequery
, userid
);
641 /* If there are sublinks, search for them and process their RTEs */
642 if (qry
->hasSubLinks
)
643 query_tree_walker(qry
, setRuleCheckAsUser_walker
, (void *) &userid
,
644 QTW_IGNORE_RC_SUBQUERIES
);
649 * Change the firing semantics of an existing rule.
652 EnableDisableRule(Relation rel
, const char *rulename
,
655 Relation pg_rewrite_desc
;
656 Oid owningRel
= RelationGetRelid(rel
);
657 Oid eventRelationOid
;
659 bool changed
= false;
662 * Find the rule tuple to change.
664 pg_rewrite_desc
= heap_open(RewriteRelationId
, RowExclusiveLock
);
665 ruletup
= SearchSysCacheCopy(RULERELNAME
,
666 ObjectIdGetDatum(owningRel
),
667 PointerGetDatum(rulename
),
669 if (!HeapTupleIsValid(ruletup
))
671 (errcode(ERRCODE_UNDEFINED_OBJECT
),
672 errmsg("rule \"%s\" for relation \"%s\" does not exist",
673 rulename
, get_rel_name(owningRel
))));
676 * Verify that the user has appropriate permissions.
678 eventRelationOid
= ((Form_pg_rewrite
) GETSTRUCT(ruletup
))->ev_class
;
679 Assert(eventRelationOid
== owningRel
);
680 if (!pg_class_ownercheck(eventRelationOid
, GetUserId()))
681 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
682 get_rel_name(eventRelationOid
));
685 * Change ev_enabled if it is different from the desired new state.
687 if (DatumGetChar(((Form_pg_rewrite
) GETSTRUCT(ruletup
))->ev_enabled
) !=
690 ((Form_pg_rewrite
) GETSTRUCT(ruletup
))->ev_enabled
=
691 CharGetDatum(fires_when
);
692 simple_heap_update(pg_rewrite_desc
, &ruletup
->t_self
, ruletup
);
694 /* keep system catalog indexes current */
695 CatalogUpdateIndexes(pg_rewrite_desc
, ruletup
);
700 heap_freetuple(ruletup
);
701 heap_close(pg_rewrite_desc
, RowExclusiveLock
);
704 * If we changed anything, broadcast a SI inval message to force each
705 * backend (including our own!) to rebuild relation's relcache entry.
706 * Otherwise they will fail to apply the change promptly.
709 CacheInvalidateRelcache(rel
);
714 * Rename an existing rewrite rule.
716 * This is unused code at the moment. Note that it lacks a permissions check.
720 RenameRewriteRule(Oid owningRel
, const char *oldName
,
723 Relation pg_rewrite_desc
;
726 pg_rewrite_desc
= heap_open(RewriteRelationId
, RowExclusiveLock
);
728 ruletup
= SearchSysCacheCopy(RULERELNAME
,
729 ObjectIdGetDatum(owningRel
),
730 PointerGetDatum(oldName
),
732 if (!HeapTupleIsValid(ruletup
))
734 (errcode(ERRCODE_UNDEFINED_OBJECT
),
735 errmsg("rule \"%s\" for relation \"%s\" does not exist",
736 oldName
, get_rel_name(owningRel
))));
738 /* should not already exist */
739 if (IsDefinedRewriteRule(owningRel
, newName
))
741 (errcode(ERRCODE_DUPLICATE_OBJECT
),
742 errmsg("rule \"%s\" for relation \"%s\" already exists",
743 newName
, get_rel_name(owningRel
))));
745 namestrcpy(&(((Form_pg_rewrite
) GETSTRUCT(ruletup
))->rulename
), newName
);
747 simple_heap_update(pg_rewrite_desc
, &ruletup
->t_self
, ruletup
);
749 /* keep system catalog indexes current */
750 CatalogUpdateIndexes(pg_rewrite_desc
, ruletup
);
752 heap_freetuple(ruletup
);
753 heap_close(pg_rewrite_desc
, RowExclusiveLock
);