1 /*-------------------------------------------------------------------------
4 * routines for defining a rewrite rule
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/rewrite/rewriteDefine.c
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "access/htup_details.h"
19 #include "access/multixact.h"
20 #include "access/tableam.h"
21 #include "access/transam.h"
22 #include "access/xact.h"
23 #include "catalog/catalog.h"
24 #include "catalog/dependency.h"
25 #include "catalog/heap.h"
26 #include "catalog/indexing.h"
27 #include "catalog/namespace.h"
28 #include "catalog/objectaccess.h"
29 #include "catalog/pg_inherits.h"
30 #include "catalog/pg_rewrite.h"
31 #include "catalog/storage.h"
32 #include "commands/policy.h"
33 #include "commands/tablecmds.h"
34 #include "miscadmin.h"
35 #include "nodes/nodeFuncs.h"
36 #include "parser/parse_utilcmd.h"
37 #include "rewrite/rewriteDefine.h"
38 #include "rewrite/rewriteManip.h"
39 #include "rewrite/rewriteSupport.h"
40 #include "utils/acl.h"
41 #include "utils/builtins.h"
42 #include "utils/inval.h"
43 #include "utils/lsyscache.h"
44 #include "utils/rel.h"
45 #include "utils/snapmgr.h"
46 #include "utils/syscache.h"
49 static void checkRuleResultList(List
*targetList
, TupleDesc resultDesc
,
50 bool isSelect
, bool requireColumnNameMatch
);
51 static bool setRuleCheckAsUser_walker(Node
*node
, Oid
*context
);
52 static void setRuleCheckAsUser_Query(Query
*qry
, Oid userid
);
57 * takes the arguments and inserts them as a row into the system
58 * relation "pg_rewrite"
61 InsertRule(const char *rulname
,
69 char *evqual
= nodeToString(event_qual
);
70 char *actiontree
= nodeToString((Node
*) action
);
71 Datum values
[Natts_pg_rewrite
];
72 bool nulls
[Natts_pg_rewrite
];
73 bool replaces
[Natts_pg_rewrite
];
75 Relation pg_rewrite_desc
;
81 bool is_update
= false;
84 * Set up *nulls and *values arrays
86 MemSet(nulls
, false, sizeof(nulls
));
88 namestrcpy(&rname
, rulname
);
89 values
[Anum_pg_rewrite_rulename
- 1] = NameGetDatum(&rname
);
90 values
[Anum_pg_rewrite_ev_class
- 1] = ObjectIdGetDatum(eventrel_oid
);
91 values
[Anum_pg_rewrite_ev_type
- 1] = CharGetDatum(evtype
+ '0');
92 values
[Anum_pg_rewrite_ev_enabled
- 1] = CharGetDatum(RULE_FIRES_ON_ORIGIN
);
93 values
[Anum_pg_rewrite_is_instead
- 1] = BoolGetDatum(evinstead
);
94 values
[Anum_pg_rewrite_ev_qual
- 1] = CStringGetTextDatum(evqual
);
95 values
[Anum_pg_rewrite_ev_action
- 1] = CStringGetTextDatum(actiontree
);
98 * Ready to store new pg_rewrite tuple
100 pg_rewrite_desc
= table_open(RewriteRelationId
, RowExclusiveLock
);
103 * Check to see if we are replacing an existing tuple
105 oldtup
= SearchSysCache2(RULERELNAME
,
106 ObjectIdGetDatum(eventrel_oid
),
107 PointerGetDatum(rulname
));
109 if (HeapTupleIsValid(oldtup
))
113 (errcode(ERRCODE_DUPLICATE_OBJECT
),
114 errmsg("rule \"%s\" for relation \"%s\" already exists",
115 rulname
, get_rel_name(eventrel_oid
))));
118 * When replacing, we don't need to replace every attribute
120 MemSet(replaces
, false, sizeof(replaces
));
121 replaces
[Anum_pg_rewrite_ev_type
- 1] = true;
122 replaces
[Anum_pg_rewrite_is_instead
- 1] = true;
123 replaces
[Anum_pg_rewrite_ev_qual
- 1] = true;
124 replaces
[Anum_pg_rewrite_ev_action
- 1] = true;
126 tup
= heap_modify_tuple(oldtup
, RelationGetDescr(pg_rewrite_desc
),
127 values
, nulls
, replaces
);
129 CatalogTupleUpdate(pg_rewrite_desc
, &tup
->t_self
, tup
);
131 ReleaseSysCache(oldtup
);
133 rewriteObjectId
= ((Form_pg_rewrite
) GETSTRUCT(tup
))->oid
;
138 rewriteObjectId
= GetNewOidWithIndex(pg_rewrite_desc
,
140 Anum_pg_rewrite_oid
);
141 values
[Anum_pg_rewrite_oid
- 1] = ObjectIdGetDatum(rewriteObjectId
);
143 tup
= heap_form_tuple(pg_rewrite_desc
->rd_att
, values
, nulls
);
145 CatalogTupleInsert(pg_rewrite_desc
, tup
);
151 /* If replacing, get rid of old dependencies and make new ones */
153 deleteDependencyRecordsFor(RewriteRelationId
, rewriteObjectId
, false);
156 * Install dependency on rule's relation to ensure it will go away on
157 * relation deletion. If the rule is ON SELECT, make the dependency
158 * implicit --- this prevents deleting a view's SELECT rule. Other kinds
159 * of rules can be AUTO.
161 myself
.classId
= RewriteRelationId
;
162 myself
.objectId
= rewriteObjectId
;
163 myself
.objectSubId
= 0;
165 referenced
.classId
= RelationRelationId
;
166 referenced
.objectId
= eventrel_oid
;
167 referenced
.objectSubId
= 0;
169 recordDependencyOn(&myself
, &referenced
,
170 (evtype
== CMD_SELECT
) ? DEPENDENCY_INTERNAL
: DEPENDENCY_AUTO
);
173 * Also install dependencies on objects referenced in action and qual.
175 recordDependencyOnExpr(&myself
, (Node
*) action
, NIL
,
178 if (event_qual
!= NULL
)
180 /* Find query containing OLD/NEW rtable entries */
181 Query
*qry
= linitial_node(Query
, action
);
183 qry
= getInsertSelectQuery(qry
, NULL
);
184 recordDependencyOnExpr(&myself
, event_qual
, qry
->rtable
,
188 /* Post creation hook for new rule */
189 InvokeObjectPostCreateHook(RewriteRelationId
, rewriteObjectId
, 0);
191 table_close(pg_rewrite_desc
, RowExclusiveLock
);
193 return rewriteObjectId
;
198 * Execute a CREATE RULE command.
201 DefineRule(RuleStmt
*stmt
, const char *queryString
)
207 /* Parse analysis. */
208 transformRuleStmt(stmt
, queryString
, &actions
, &whereClause
);
211 * Find and lock the relation. Lock level should match
212 * DefineQueryRewrite.
214 relId
= RangeVarGetRelid(stmt
->relation
, AccessExclusiveLock
, false);
216 /* ... and execute */
217 return DefineQueryRewrite(stmt
->rulename
,
231 * This is essentially the same as DefineRule() except that the rule's
232 * action and qual have already been passed through parse analysis.
235 DefineQueryRewrite(const char *rulename
,
243 Relation event_relation
;
246 bool RelisBecomingView
= false;
247 Oid ruleId
= InvalidOid
;
248 ObjectAddress address
;
251 * If we are installing an ON SELECT rule, we had better grab
252 * AccessExclusiveLock to ensure no SELECTs are currently running on the
253 * event relation. For other types of rules, it would be sufficient to
254 * grab ShareRowExclusiveLock to lock out insert/update/delete actions and
255 * to ensure that we lock out current CREATE RULE statements; but because
256 * of race conditions in access to catalog entries, we can't do that yet.
258 * Note that this lock level should match the one used in DefineRule.
260 event_relation
= table_open(event_relid
, AccessExclusiveLock
);
263 * Verify relation is of a type that rules can sensibly be applied to.
264 * Internal callers can target materialized views, but transformRuleStmt()
265 * blocks them for users. Don't mention them in the error message.
267 if (event_relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
268 event_relation
->rd_rel
->relkind
!= RELKIND_MATVIEW
&&
269 event_relation
->rd_rel
->relkind
!= RELKIND_VIEW
&&
270 event_relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
272 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
273 errmsg("\"%s\" is not a table or view",
274 RelationGetRelationName(event_relation
))));
276 if (!allowSystemTableMods
&& IsSystemRelation(event_relation
))
278 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
279 errmsg("permission denied: \"%s\" is a system catalog",
280 RelationGetRelationName(event_relation
))));
283 * Check user has permission to apply rules to this relation.
285 if (!pg_class_ownercheck(event_relid
, GetUserId()))
286 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(event_relation
->rd_rel
->relkind
),
287 RelationGetRelationName(event_relation
));
290 * No rule actions that modify OLD or NEW
294 query
= lfirst_node(Query
, l
);
295 if (query
->resultRelation
== 0)
297 /* Don't be fooled by INSERT/SELECT */
298 if (query
!= getInsertSelectQuery(query
, NULL
))
300 if (query
->resultRelation
== PRS2_OLD_VARNO
)
302 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
303 errmsg("rule actions on OLD are not implemented"),
304 errhint("Use views or triggers instead.")));
305 if (query
->resultRelation
== PRS2_NEW_VARNO
)
307 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
308 errmsg("rule actions on NEW are not implemented"),
309 errhint("Use triggers instead.")));
312 if (event_type
== CMD_SELECT
)
315 * Rules ON SELECT are restricted to view definitions
317 * So there cannot be INSTEAD NOTHING, ...
319 if (list_length(action
) == 0)
321 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
322 errmsg("INSTEAD NOTHING rules on SELECT are not implemented"),
323 errhint("Use views instead.")));
326 * ... there cannot be multiple actions, ...
328 if (list_length(action
) > 1)
330 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
331 errmsg("multiple actions for rules on SELECT are not implemented")));
334 * ... the one action must be a SELECT, ...
336 query
= linitial_node(Query
, action
);
338 query
->commandType
!= CMD_SELECT
)
340 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
341 errmsg("rules on SELECT must have action INSTEAD SELECT")));
344 * ... it cannot contain data-modifying WITH ...
346 if (query
->hasModifyingCTE
)
348 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
349 errmsg("rules on SELECT must not contain data-modifying statements in WITH")));
352 * ... there can be no rule qual, ...
354 if (event_qual
!= NULL
)
356 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
357 errmsg("event qualifications are not implemented for rules on SELECT")));
360 * ... the targetlist of the SELECT action must exactly match the
361 * event relation, ...
363 checkRuleResultList(query
->targetList
,
364 RelationGetDescr(event_relation
),
366 event_relation
->rd_rel
->relkind
!=
370 * ... there must not be another ON SELECT rule already ...
372 if (!replace
&& event_relation
->rd_rules
!= NULL
)
376 for (i
= 0; i
< event_relation
->rd_rules
->numLocks
; i
++)
380 rule
= event_relation
->rd_rules
->rules
[i
];
381 if (rule
->event
== CMD_SELECT
)
383 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
384 errmsg("\"%s\" is already a view",
385 RelationGetRelationName(event_relation
))));
390 * ... and finally the rule must be named _RETURN.
392 if (strcmp(rulename
, ViewSelectRuleName
) != 0)
395 * In versions before 7.3, the expected name was _RETviewname. For
396 * backwards compatibility with old pg_dump output, accept that
397 * and silently change it to _RETURN. Since this is just a quick
398 * backwards-compatibility hack, limit the number of characters
399 * checked to a few less than NAMEDATALEN; this saves having to
400 * worry about where a multibyte character might have gotten
403 if (strncmp(rulename
, "_RET", 4) != 0 ||
404 strncmp(rulename
+ 4, RelationGetRelationName(event_relation
),
405 NAMEDATALEN
- 4 - 4) != 0)
407 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
408 errmsg("view rule for \"%s\" must be named \"%s\"",
409 RelationGetRelationName(event_relation
),
410 ViewSelectRuleName
)));
411 rulename
= pstrdup(ViewSelectRuleName
);
415 * Are we converting a relation to a view?
417 * If so, check that the relation is empty because the storage for the
418 * relation is going to be deleted. Also insist that the rel not be
419 * involved in partitioning, nor have any triggers, indexes, child or
420 * parent tables, RLS policies, or RLS enabled. (Note: some of these
421 * tests are too strict, because they will reject relations that once
422 * had such but don't anymore. But we don't really care, because this
423 * whole business of converting relations to views is just an obsolete
424 * kluge to allow dump/reload of views that participate in circular
427 * Also ensure the relation isn't being manipulated in any outer SQL
428 * command of our own session.
430 if (event_relation
->rd_rel
->relkind
!= RELKIND_VIEW
&&
431 event_relation
->rd_rel
->relkind
!= RELKIND_MATVIEW
)
433 TableScanDesc scanDesc
;
435 TupleTableSlot
*slot
;
437 CheckTableNotInUse(event_relation
, "CREATE RULE");
439 if (event_relation
->rd_rel
->relkind
== RELKIND_PARTITIONED_TABLE
)
441 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
442 errmsg("cannot convert partitioned table \"%s\" to a view",
443 RelationGetRelationName(event_relation
))));
445 /* only case left: */
446 Assert(event_relation
->rd_rel
->relkind
== RELKIND_RELATION
);
448 if (event_relation
->rd_rel
->relispartition
)
450 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
451 errmsg("cannot convert partition \"%s\" to a view",
452 RelationGetRelationName(event_relation
))));
454 snapshot
= RegisterSnapshot(GetLatestSnapshot());
455 scanDesc
= table_beginscan(event_relation
, snapshot
, 0, NULL
);
456 slot
= table_slot_create(event_relation
, NULL
);
457 if (table_scan_getnextslot(scanDesc
, ForwardScanDirection
, slot
))
459 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
460 errmsg("could not convert table \"%s\" to a view because it is not empty",
461 RelationGetRelationName(event_relation
))));
462 ExecDropSingleTupleTableSlot(slot
);
463 table_endscan(scanDesc
);
464 UnregisterSnapshot(snapshot
);
466 if (event_relation
->rd_rel
->relhastriggers
)
468 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
469 errmsg("could not convert table \"%s\" to a view because it has triggers",
470 RelationGetRelationName(event_relation
)),
471 errhint("In particular, the table cannot be involved in any foreign key relationships.")));
473 if (event_relation
->rd_rel
->relhasindex
)
475 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
476 errmsg("could not convert table \"%s\" to a view because it has indexes",
477 RelationGetRelationName(event_relation
))));
479 if (event_relation
->rd_rel
->relhassubclass
)
481 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
482 errmsg("could not convert table \"%s\" to a view because it has child tables",
483 RelationGetRelationName(event_relation
))));
485 if (has_superclass(RelationGetRelid(event_relation
)))
487 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
488 errmsg("could not convert table \"%s\" to a view because it has parent tables",
489 RelationGetRelationName(event_relation
))));
491 if (event_relation
->rd_rel
->relrowsecurity
)
493 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
494 errmsg("could not convert table \"%s\" to a view because it has row security enabled",
495 RelationGetRelationName(event_relation
))));
497 if (relation_has_policies(event_relation
))
499 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
500 errmsg("could not convert table \"%s\" to a view because it has row security policies",
501 RelationGetRelationName(event_relation
))));
503 RelisBecomingView
= true;
509 * For non-SELECT rules, a RETURNING list can appear in at most one of
510 * the actions ... and there can't be any RETURNING list at all in a
511 * conditional or non-INSTEAD rule. (Actually, there can be at most
512 * one RETURNING list across all rules on the same event, but it seems
513 * best to enforce that at rule expansion time.) If there is a
514 * RETURNING list, it must match the event relation.
516 bool haveReturning
= false;
520 query
= lfirst_node(Query
, l
);
522 if (!query
->returningList
)
526 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
527 errmsg("cannot have multiple RETURNING lists in a rule")));
528 haveReturning
= true;
529 if (event_qual
!= NULL
)
531 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
532 errmsg("RETURNING lists are not supported in conditional rules")));
535 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
536 errmsg("RETURNING lists are not supported in non-INSTEAD rules")));
537 checkRuleResultList(query
->returningList
,
538 RelationGetDescr(event_relation
),
543 * And finally, if it's not an ON SELECT rule then it must *not* be
544 * named _RETURN. This prevents accidentally or maliciously replacing
545 * a view's ON SELECT rule with some other kind of rule.
547 if (strcmp(rulename
, ViewSelectRuleName
) == 0)
549 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
550 errmsg("non-view rule for \"%s\" must not be named \"%s\"",
551 RelationGetRelationName(event_relation
),
552 ViewSelectRuleName
)));
556 * This rule is allowed - prepare to install it.
559 /* discard rule if it's null action and not INSTEAD; it's a no-op */
560 if (action
!= NIL
|| is_instead
)
562 ruleId
= InsertRule(rulename
,
571 * Set pg_class 'relhasrules' field true for event relation.
573 * Important side effect: an SI notice is broadcast to force all
574 * backends (including me!) to update relcache entries with the new
577 SetRelationRuleStatus(event_relid
, true);
580 /* ---------------------------------------------------------------------
581 * If the relation is becoming a view:
582 * - delete the associated storage files
583 * - get rid of any system attributes in pg_attribute; a view shouldn't
585 * - remove the toast table; there is no need for it anymore, and its
586 * presence would make vacuum slightly more complicated
587 * - set relkind to RELKIND_VIEW, and adjust other pg_class fields
588 * to be appropriate for a view
590 * NB: we had better have AccessExclusiveLock to do this ...
591 * ---------------------------------------------------------------------
593 if (RelisBecomingView
)
595 Relation relationRelation
;
598 Form_pg_class classForm
;
600 relationRelation
= table_open(RelationRelationId
, RowExclusiveLock
);
601 toastrelid
= event_relation
->rd_rel
->reltoastrelid
;
603 /* drop storage while table still looks like a table */
604 RelationDropStorage(event_relation
);
605 DeleteSystemAttributeTuples(event_relid
);
608 * Drop the toast table if any. (This won't take care of updating the
609 * toast fields in the relation's own pg_class entry; we handle that
612 if (OidIsValid(toastrelid
))
614 ObjectAddress toastobject
;
617 * Delete the dependency of the toast relation on the main
618 * relation so we can drop the former without dropping the latter.
620 deleteDependencyRecordsFor(RelationRelationId
, toastrelid
,
623 /* Make deletion of dependency record visible */
624 CommandCounterIncrement();
626 /* Now drop toast table, including its index */
627 toastobject
.classId
= RelationRelationId
;
628 toastobject
.objectId
= toastrelid
;
629 toastobject
.objectSubId
= 0;
630 performDeletion(&toastobject
, DROP_RESTRICT
,
631 PERFORM_DELETION_INTERNAL
);
635 * SetRelationRuleStatus may have updated the pg_class row, so we must
636 * advance the command counter before trying to update it again.
638 CommandCounterIncrement();
641 * Fix pg_class entry to look like a normal view's, including setting
642 * the correct relkind and removal of reltoastrelid of the toast table
643 * we potentially removed above.
645 classTup
= SearchSysCacheCopy1(RELOID
, ObjectIdGetDatum(event_relid
));
646 if (!HeapTupleIsValid(classTup
))
647 elog(ERROR
, "cache lookup failed for relation %u", event_relid
);
648 classForm
= (Form_pg_class
) GETSTRUCT(classTup
);
650 classForm
->relam
= InvalidOid
;
651 classForm
->reltablespace
= InvalidOid
;
652 classForm
->relpages
= 0;
653 classForm
->reltuples
= 0;
654 classForm
->relallvisible
= 0;
655 classForm
->reltoastrelid
= InvalidOid
;
656 classForm
->relhasindex
= false;
657 classForm
->relkind
= RELKIND_VIEW
;
658 classForm
->relfrozenxid
= InvalidTransactionId
;
659 classForm
->relminmxid
= InvalidMultiXactId
;
660 classForm
->relreplident
= REPLICA_IDENTITY_NOTHING
;
662 CatalogTupleUpdate(relationRelation
, &classTup
->t_self
, classTup
);
664 heap_freetuple(classTup
);
665 table_close(relationRelation
, RowExclusiveLock
);
668 ObjectAddressSet(address
, RewriteRelationId
, ruleId
);
670 /* Close rel, but keep lock till commit... */
671 table_close(event_relation
, NoLock
);
677 * checkRuleResultList
678 * Verify that targetList produces output compatible with a tupledesc
680 * The targetList might be either a SELECT targetlist, or a RETURNING list;
681 * isSelect tells which. This is used for choosing error messages.
683 * A SELECT targetlist may optionally require that column names match.
686 checkRuleResultList(List
*targetList
, TupleDesc resultDesc
, bool isSelect
,
687 bool requireColumnNameMatch
)
692 /* Only a SELECT may require a column name match. */
693 Assert(isSelect
|| !requireColumnNameMatch
);
696 foreach(tllist
, targetList
)
698 TargetEntry
*tle
= (TargetEntry
*) lfirst(tllist
);
701 Form_pg_attribute attr
;
704 /* resjunk entries may be ignored */
708 if (i
> resultDesc
->natts
)
710 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
712 errmsg("SELECT rule's target list has too many entries") :
713 errmsg("RETURNING list has too many entries")));
715 attr
= TupleDescAttr(resultDesc
, i
- 1);
716 attname
= NameStr(attr
->attname
);
719 * Disallow dropped columns in the relation. This is not really
720 * expected to happen when creating an ON SELECT rule. It'd be
721 * possible if someone tried to convert a relation with dropped
722 * columns to a view, but the only case we care about supporting
723 * table-to-view conversion for is pg_dump, and pg_dump won't do that.
725 * Unfortunately, the situation is also possible when adding a rule
726 * with RETURNING to a regular table, and rejecting that case is
727 * altogether more annoying. In principle we could support it by
728 * modifying the targetlist to include dummy NULL columns
729 * corresponding to the dropped columns in the tupdesc. However,
730 * places like ruleutils.c would have to be fixed to not process such
731 * entries, and that would take an uncertain and possibly rather large
732 * amount of work. (Note we could not dodge that by marking the dummy
733 * columns resjunk, since it's precisely the non-resjunk tlist columns
734 * that are expected to correspond to table columns.)
736 if (attr
->attisdropped
)
738 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
740 errmsg("cannot convert relation containing dropped columns to view") :
741 errmsg("cannot create a RETURNING list for a relation containing dropped columns")));
743 /* Check name match if required; no need for two error texts here */
744 if (requireColumnNameMatch
&& strcmp(tle
->resname
, attname
) != 0)
746 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
747 errmsg("SELECT rule's target entry %d has different column name from column \"%s\"",
749 errdetail("SELECT target entry is named \"%s\".",
752 /* Check type match. */
753 tletypid
= exprType((Node
*) tle
->expr
);
754 if (attr
->atttypid
!= tletypid
)
756 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
758 errmsg("SELECT rule's target entry %d has different type from column \"%s\"",
760 errmsg("RETURNING list's entry %d has different type from column \"%s\"",
763 errdetail("SELECT target entry has type %s, but column has type %s.",
764 format_type_be(tletypid
),
765 format_type_be(attr
->atttypid
)) :
766 errdetail("RETURNING list entry has type %s, but column has type %s.",
767 format_type_be(tletypid
),
768 format_type_be(attr
->atttypid
))));
771 * Allow typmods to be different only if one of them is -1, ie,
772 * "unspecified". This is necessary for cases like "numeric", where
773 * the table will have a filled-in default length but the select
774 * rule's expression will probably have typmod = -1.
776 tletypmod
= exprTypmod((Node
*) tle
->expr
);
777 if (attr
->atttypmod
!= tletypmod
&&
778 attr
->atttypmod
!= -1 && tletypmod
!= -1)
780 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
782 errmsg("SELECT rule's target entry %d has different size from column \"%s\"",
784 errmsg("RETURNING list's entry %d has different size from column \"%s\"",
787 errdetail("SELECT target entry has type %s, but column has type %s.",
788 format_type_with_typemod(tletypid
, tletypmod
),
789 format_type_with_typemod(attr
->atttypid
,
791 errdetail("RETURNING list entry has type %s, but column has type %s.",
792 format_type_with_typemod(tletypid
, tletypmod
),
793 format_type_with_typemod(attr
->atttypid
,
797 if (i
!= resultDesc
->natts
)
799 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
801 errmsg("SELECT rule's target list has too few entries") :
802 errmsg("RETURNING list has too few entries")));
807 * Recursively scan a query or expression tree and set the checkAsUser
808 * field to the given userid in all rtable entries.
810 * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD
811 * RTE entry will be overridden when the view rule is expanded, and the
812 * checkAsUser field of the NEW entry is irrelevant because that entry's
813 * requiredPerms bits will always be zero. However, for other types of rules
814 * it's important to set these fields to match the rule owner. So we just set
818 setRuleCheckAsUser(Node
*node
, Oid userid
)
820 (void) setRuleCheckAsUser_walker(node
, &userid
);
824 setRuleCheckAsUser_walker(Node
*node
, Oid
*context
)
828 if (IsA(node
, Query
))
830 setRuleCheckAsUser_Query((Query
*) node
, *context
);
833 return expression_tree_walker(node
, setRuleCheckAsUser_walker
,
838 setRuleCheckAsUser_Query(Query
*qry
, Oid userid
)
842 /* Set all the RTEs in this query node */
843 foreach(l
, qry
->rtable
)
845 RangeTblEntry
*rte
= (RangeTblEntry
*) lfirst(l
);
847 if (rte
->rtekind
== RTE_SUBQUERY
)
849 /* Recurse into subquery in FROM */
850 setRuleCheckAsUser_Query(rte
->subquery
, userid
);
853 rte
->checkAsUser
= userid
;
856 /* Recurse into subquery-in-WITH */
857 foreach(l
, qry
->cteList
)
859 CommonTableExpr
*cte
= (CommonTableExpr
*) lfirst(l
);
861 setRuleCheckAsUser_Query(castNode(Query
, cte
->ctequery
), userid
);
864 /* If there are sublinks, search for them and process their RTEs */
865 if (qry
->hasSubLinks
)
866 query_tree_walker(qry
, setRuleCheckAsUser_walker
, (void *) &userid
,
867 QTW_IGNORE_RC_SUBQUERIES
);
872 * Change the firing semantics of an existing rule.
875 EnableDisableRule(Relation rel
, const char *rulename
,
878 Relation pg_rewrite_desc
;
879 Oid owningRel
= RelationGetRelid(rel
);
880 Oid eventRelationOid
;
882 Form_pg_rewrite ruleform
;
883 bool changed
= false;
886 * Find the rule tuple to change.
888 pg_rewrite_desc
= table_open(RewriteRelationId
, RowExclusiveLock
);
889 ruletup
= SearchSysCacheCopy2(RULERELNAME
,
890 ObjectIdGetDatum(owningRel
),
891 PointerGetDatum(rulename
));
892 if (!HeapTupleIsValid(ruletup
))
894 (errcode(ERRCODE_UNDEFINED_OBJECT
),
895 errmsg("rule \"%s\" for relation \"%s\" does not exist",
896 rulename
, get_rel_name(owningRel
))));
898 ruleform
= (Form_pg_rewrite
) GETSTRUCT(ruletup
);
901 * Verify that the user has appropriate permissions.
903 eventRelationOid
= ruleform
->ev_class
;
904 Assert(eventRelationOid
== owningRel
);
905 if (!pg_class_ownercheck(eventRelationOid
, GetUserId()))
906 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(eventRelationOid
)),
907 get_rel_name(eventRelationOid
));
910 * Change ev_enabled if it is different from the desired new state.
912 if (DatumGetChar(ruleform
->ev_enabled
) !=
915 ruleform
->ev_enabled
= CharGetDatum(fires_when
);
916 CatalogTupleUpdate(pg_rewrite_desc
, &ruletup
->t_self
, ruletup
);
921 InvokeObjectPostAlterHook(RewriteRelationId
, ruleform
->oid
, 0);
923 heap_freetuple(ruletup
);
924 table_close(pg_rewrite_desc
, RowExclusiveLock
);
927 * If we changed anything, broadcast a SI inval message to force each
928 * backend (including our own!) to rebuild relation's relcache entry.
929 * Otherwise they will fail to apply the change promptly.
932 CacheInvalidateRelcache(rel
);
937 * Perform permissions and integrity checks before acquiring a relation lock.
940 RangeVarCallbackForRenameRule(const RangeVar
*rv
, Oid relid
, Oid oldrelid
,
946 tuple
= SearchSysCache1(RELOID
, ObjectIdGetDatum(relid
));
947 if (!HeapTupleIsValid(tuple
))
948 return; /* concurrently dropped */
949 form
= (Form_pg_class
) GETSTRUCT(tuple
);
951 /* only tables and views can have rules */
952 if (form
->relkind
!= RELKIND_RELATION
&&
953 form
->relkind
!= RELKIND_VIEW
&&
954 form
->relkind
!= RELKIND_PARTITIONED_TABLE
)
956 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
957 errmsg("\"%s\" is not a table or view", rv
->relname
)));
959 if (!allowSystemTableMods
&& IsSystemClass(relid
, form
))
961 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
962 errmsg("permission denied: \"%s\" is a system catalog",
965 /* you must own the table to rename one of its rules */
966 if (!pg_class_ownercheck(relid
, GetUserId()))
967 aclcheck_error(ACLCHECK_NOT_OWNER
, get_relkind_objtype(get_rel_relkind(relid
)), rv
->relname
);
969 ReleaseSysCache(tuple
);
973 * Rename an existing rewrite rule.
976 RenameRewriteRule(RangeVar
*relation
, const char *oldName
,
981 Relation pg_rewrite_desc
;
983 Form_pg_rewrite ruleform
;
985 ObjectAddress address
;
988 * Look up name, check permissions, and acquire lock (which we will NOT
989 * release until end of transaction).
991 relid
= RangeVarGetRelidExtended(relation
, AccessExclusiveLock
,
993 RangeVarCallbackForRenameRule
,
996 /* Have lock already, so just need to build relcache entry. */
997 targetrel
= relation_open(relid
, NoLock
);
999 /* Prepare to modify pg_rewrite */
1000 pg_rewrite_desc
= table_open(RewriteRelationId
, RowExclusiveLock
);
1002 /* Fetch the rule's entry (it had better exist) */
1003 ruletup
= SearchSysCacheCopy2(RULERELNAME
,
1004 ObjectIdGetDatum(relid
),
1005 PointerGetDatum(oldName
));
1006 if (!HeapTupleIsValid(ruletup
))
1008 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1009 errmsg("rule \"%s\" for relation \"%s\" does not exist",
1010 oldName
, RelationGetRelationName(targetrel
))));
1011 ruleform
= (Form_pg_rewrite
) GETSTRUCT(ruletup
);
1012 ruleOid
= ruleform
->oid
;
1014 /* rule with the new name should not already exist */
1015 if (IsDefinedRewriteRule(relid
, newName
))
1017 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1018 errmsg("rule \"%s\" for relation \"%s\" already exists",
1019 newName
, RelationGetRelationName(targetrel
))));
1022 * We disallow renaming ON SELECT rules, because they should always be
1025 if (ruleform
->ev_type
== CMD_SELECT
+ '0')
1027 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1028 errmsg("renaming an ON SELECT rule is not allowed")));
1030 /* OK, do the update */
1031 namestrcpy(&(ruleform
->rulename
), newName
);
1033 CatalogTupleUpdate(pg_rewrite_desc
, &ruletup
->t_self
, ruletup
);
1035 heap_freetuple(ruletup
);
1036 table_close(pg_rewrite_desc
, RowExclusiveLock
);
1039 * Invalidate relation's relcache entry so that other backends (and this
1040 * one too!) are sent SI message to make them rebuild relcache entries.
1041 * (Ideally this should happen automatically...)
1043 CacheInvalidateRelcache(targetrel
);
1045 ObjectAddressSet(address
, RewriteRelationId
, ruleOid
);
1048 * Close rel, but keep exclusive lock!
1050 relation_close(targetrel
, NoLock
);