diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/alter.c | 5 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 10 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 99 |
3 files changed, 101 insertions, 13 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index c2d4bb3ed4d..269d19cea6c 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -51,6 +51,7 @@ #include "commands/user.h" #include "parser/parse_func.h" #include "miscadmin.h" +#include "rewrite/rewriteDefine.h" #include "tcop/utility.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -324,6 +325,10 @@ ExecRenameStmt(RenameStmt *stmt) case OBJECT_ATTRIBUTE: return renameatt(stmt); + case OBJECT_RULE: + return RenameRewriteRule(stmt->relation, stmt->subname, + stmt->newname); + case OBJECT_TRIGGER: return renametrig(stmt); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 342b7964242..fee05311c5c 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -7003,6 +7003,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = true; $$ = (Node *)n; } + | ALTER RULE name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_RULE; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER TRIGGER name ON qualified_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index ac724c3964a..b37f36b3e67 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -752,37 +752,98 @@ EnableDisableRule(Relation rel, const char *rulename, /* + * Perform permissions and integrity checks before acquiring a relation lock. + */ +static void +RangeVarCallbackForRenameRule(const RangeVar *rv, Oid relid, Oid oldrelid, + void *arg) +{ + HeapTuple tuple; + Form_pg_class form; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + return; /* concurrently dropped */ + form = (Form_pg_class) GETSTRUCT(tuple); + + /* only tables and views can have rules */ + if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table or view", rv->relname))); + + if (!allowSystemTableMods && IsSystemClass(form)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + rv->relname))); + + /* you must own the table to rename one of its rules */ + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rv->relname); + + ReleaseSysCache(tuple); +} + +/* * Rename an existing rewrite rule. - * - * This is unused code at the moment. Note that it lacks a permissions check. */ -#ifdef NOT_USED -void -RenameRewriteRule(Oid owningRel, const char *oldName, +Oid +RenameRewriteRule(RangeVar *relation, const char *oldName, const char *newName) { + Oid relid; + Relation targetrel; Relation pg_rewrite_desc; HeapTuple ruletup; + Form_pg_rewrite ruleform; + Oid ruleOid; + /* + * Look up name, check permissions, and acquire lock (which we will NOT + * release until end of transaction). + */ + relid = RangeVarGetRelidExtended(relation, AccessExclusiveLock, + false, false, + RangeVarCallbackForRenameRule, + NULL); + + /* Have lock already, so just need to build relcache entry. */ + targetrel = relation_open(relid, NoLock); + + /* Prepare to modify pg_rewrite */ pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock); + /* Fetch the rule's entry (it had better exist) */ ruletup = SearchSysCacheCopy2(RULERELNAME, - ObjectIdGetDatum(owningRel), + ObjectIdGetDatum(relid), PointerGetDatum(oldName)); if (!HeapTupleIsValid(ruletup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", - oldName, get_rel_name(owningRel)))); + oldName, RelationGetRelationName(targetrel)))); + ruleform = (Form_pg_rewrite) GETSTRUCT(ruletup); + ruleOid = HeapTupleGetOid(ruletup); - /* should not already exist */ - if (IsDefinedRewriteRule(owningRel, newName)) + /* rule with the new name should not already exist */ + if (IsDefinedRewriteRule(relid, newName)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("rule \"%s\" for relation \"%s\" already exists", - newName, get_rel_name(owningRel)))); + newName, RelationGetRelationName(targetrel)))); + + /* + * We disallow renaming ON SELECT rules, because they should always be + * named "_RETURN". + */ + if (ruleform->ev_type == CMD_SELECT + '0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("renaming an ON SELECT rule is not allowed"))); - namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName); + /* OK, do the update */ + namestrcpy(&(ruleform->rulename), newName); simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup); @@ -791,6 +852,18 @@ RenameRewriteRule(Oid owningRel, const char *oldName, heap_freetuple(ruletup); heap_close(pg_rewrite_desc, RowExclusiveLock); -} -#endif + /* + * Invalidate relation's relcache entry so that other backends (and this + * one too!) are sent SI message to make them rebuild relcache entries. + * (Ideally this should happen automatically...) + */ + CacheInvalidateRelcache(targetrel); + + /* + * Close rel, but keep exclusive lock! + */ + relation_close(targetrel, NoLock); + + return ruleOid; +} |