aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/alter.c5
-rw-r--r--src/backend/parser/gram.y10
-rw-r--r--src/backend/rewrite/rewriteDefine.c99
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;
+}