diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index b3fc57d..c61c62f 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -25,6 +25,7 @@ Complete list of usable sgml source files in this directory.
+
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
index 381ea3e..7e36aef 100644
--- a/doc/src/sgml/ref/create_rule.sgml
+++ b/doc/src/sgml/ref/create_rule.sgml
@@ -284,4 +284,12 @@ UPDATE mytable SET name = 'foo' WHERE id = 42;
entire query rewrite system.
+
+
+ See Also
+
+
+
+
+
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index fe90227..5b0c774 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -53,6 +53,7 @@
&alterOperatorClass;
&alterOperatorFamily;
&alterRole;
+ &alterRule;
&alterSchema;
&alterSequence;
&alterServer;
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index c2d4bb3..3cc6e9a 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"
@@ -307,6 +308,9 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_ROLE:
return RenameRole(stmt->subname, stmt->newname);
+ case OBJECT_RULE:
+ return RenameRewriteRule(stmt->relation, stmt->subname, stmt->newname);
+
case OBJECT_SCHEMA:
return RenameSchema(stmt->subname, stmt->newname);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 828e110..33ac554 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -7029,6 +7029,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->missing_ok = false;
$$ = (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 USER RoleId RENAME TO RoleId
{
RenameStmt *n = makeNode(RenameStmt);
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index ac724c3..dbdad19 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -753,35 +753,69 @@ EnableDisableRule(Relation rel, const char *rulename,
/*
* 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)
{
Relation pg_rewrite_desc;
HeapTuple ruletup;
+ Oid owningRel;
+ char owningRelKind;
+ Oid ruleOid = InvalidOid;
+
+ owningRel = RangeVarGetRelid(relation, AccessExclusiveLock, false);
+
+ owningRelKind = get_rel_relkind(owningRel);
+
+ /*
+ * verify relation is of a type that rules can be applied to
+ */
+ if (owningRelKind != RELKIND_RELATION &&
+ owningRelKind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table or view",
+ get_rel_name(owningRel))));
+
+ /*
+ * check if user has permission to rename rule on this relation
+ */
+ if (!pg_class_ownercheck(owningRel, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ get_rel_name(owningRel));
pg_rewrite_desc = heap_open(RewriteRelationId, RowExclusiveLock);
ruletup = SearchSysCacheCopy2(RULERELNAME,
ObjectIdGetDatum(owningRel),
PointerGetDatum(oldName));
+
+ /* old rule should already exist */
if (!HeapTupleIsValid(ruletup))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("rule \"%s\" for relation \"%s\" does not exist",
oldName, get_rel_name(owningRel))));
- /* should not already exist */
+ ruleOid = HeapTupleGetOid(ruletup);
+
+ /* rule with the new name should not already exist */
if (IsDefinedRewriteRule(owningRel, newName))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("rule \"%s\" for relation \"%s\" already exists",
newName, get_rel_name(owningRel))));
+ /*
+ * rename is not allowed for ON SELECT rule, because it's always
+ * named "_RETURN".
+ */
+ if (((Form_pg_rewrite) GETSTRUCT(ruletup))->ev_type - '0' == CMD_SELECT)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("rename not allowed for ON SELECT rule")));
+
namestrcpy(&(((Form_pg_rewrite) GETSTRUCT(ruletup))->rulename), newName);
simple_heap_update(pg_rewrite_desc, &ruletup->t_self, ruletup);
@@ -791,6 +825,6 @@ RenameRewriteRule(Oid owningRel, const char *oldName,
heap_freetuple(ruletup);
heap_close(pg_rewrite_desc, RowExclusiveLock);
-}
-#endif
+ return ruleOid;
+}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index f7d8466..0dbb669 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -925,7 +925,7 @@ psql_completion(char *text, int start, int end)
{"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
"EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
- "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
+ "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
"USER", "USER MAPPING FOR", "VIEW", NULL};
@@ -1681,6 +1681,22 @@ psql_completion(char *text, int start, int end)
pg_strcasecmp(prev_wd, "USER") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_roles);
+ /* ALTER RULE */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "RULE") == 0)
+ COMPLETE_WITH_CONST("ON");
+
+ /* ALTER RULE ON */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "RULE") == 0)
+ COMPLETE_WITH_CONST("RENAME TO");
+
+ /* If we have ALTER RULE ON, then add the correct viewname */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "RULE") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
+
/* BEGIN, END, ABORT */
else if (pg_strcasecmp(prev_wd, "BEGIN") == 0 ||
pg_strcasecmp(prev_wd, "END") == 0 ||
diff --git a/src/include/rewrite/rewriteDefine.h b/src/include/rewrite/rewriteDefine.h
index dda267b..72921c3 100644
--- a/src/include/rewrite/rewriteDefine.h
+++ b/src/include/rewrite/rewriteDefine.h
@@ -32,7 +32,7 @@ extern Oid DefineQueryRewrite(char *rulename,
bool replace,
List *action);
-extern void RenameRewriteRule(Oid owningRel, const char *oldName,
+extern Oid RenameRewriteRule(RangeVar* relation, const char *oldName,
const char *newName);
extern void setRuleCheckAsUser(Node *node, Oid userid);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a235571..7e79815 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1693,3 +1693,42 @@ Rules:
ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
Has OIDs: no
+--
+-- check alter rename rule
+--
+CREATE TABLE rule_t1 (a INT);
+CREATE TABLE rule_t2 (a INT);
+CREATE RULE "_RETURN" AS
+ ON SELECT TO rule_t1
+ DO INSTEAD
+ SELECT * FROM rule_t2;
+CREATE RULE InsertRule AS
+ ON INSERT TO rule_t1
+ DO INSTEAD
+ INSERT INTO rule_t2 VALUES(new.a);
+ALTER RULE InsertRule ON rule_t1 RENAME to NewInsertRule;
+INSERT INTO rule_t1 VALUES(1);
+SELECT * FROM rule_t1;
+ a
+---
+ 1
+(1 row)
+
+--
+-- error conditions for alter rename rule
+--
+ALTER RULE NewInsertRule ON rule_t1 RENAME TO "_RETURN"; -- rule already exists error
+ERROR: rule "_RETURN" for relation "rule_t1" already exists
+ALTER RULE "_RETURN" ON rule_t1 RENAME TO abc; -- ON SELECT rule can not be renamed error
+ERROR: rename not allowed for ON SELECT rule
+ALTER RULE InsertRule ON rule_t1 RENAME TO NewInsertRule; -- NO rule exists error
+ERROR: rule "insertrule" for relation "rule_t1" does not exist
+ALTER RULE NewInsertRule ON notable RENAME TO InsertRule; -- table does not exist error
+ERROR: relation "notable" does not exist
+CREATE TYPE item1 as();
+ALTER RULE InsertRule ON item1 RENAME to NewInsertRule; -- target item should be view or table
+ERROR: "item1" is not a table or view
+DROP RULE NewInsertRule ON rule_t1; -- drop altered rule
+DROP TYPE item1;
+DROP VIEW rule_t1;
+DROP TABLE rule_t2;
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 458c2f0..abdd763 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -968,3 +968,40 @@ update rules_src set f2 = f2 / 10;
select * from rules_src;
select * from rules_log;
\d+ rules_src
+
+
+--
+-- check alter rename rule
+--
+CREATE TABLE rule_t1 (a INT);
+CREATE TABLE rule_t2 (a INT);
+
+CREATE RULE "_RETURN" AS
+ ON SELECT TO rule_t1
+ DO INSTEAD
+ SELECT * FROM rule_t2;
+
+CREATE RULE InsertRule AS
+ ON INSERT TO rule_t1
+ DO INSTEAD
+ INSERT INTO rule_t2 VALUES(new.a);
+
+ALTER RULE InsertRule ON rule_t1 RENAME to NewInsertRule;
+
+INSERT INTO rule_t1 VALUES(1);
+SELECT * FROM rule_t1;
+
+--
+-- error conditions for alter rename rule
+--
+ALTER RULE NewInsertRule ON rule_t1 RENAME TO "_RETURN"; -- rule already exists error
+ALTER RULE "_RETURN" ON rule_t1 RENAME TO abc; -- ON SELECT rule can not be renamed error
+ALTER RULE InsertRule ON rule_t1 RENAME TO NewInsertRule; -- NO rule exists error
+ALTER RULE NewInsertRule ON notable RENAME TO InsertRule; -- table does not exist error
+CREATE TYPE item1 as();
+ALTER RULE InsertRule ON item1 RENAME to NewInsertRule; -- target item should be view or table
+
+DROP RULE NewInsertRule ON rule_t1; -- drop altered rule
+DROP TYPE item1;
+DROP VIEW rule_t1;
+DROP TABLE rule_t2;
\ No newline at end of file
diff --git a/doc/src/sgml/ref/alter_rule.sgml b/doc/src/sgml/ref/alter_rule.sgml
new file mode 100644
index 0000000..3861e4a
--- /dev/null
+++ b/doc/src/sgml/ref/alter_rule.sgml
@@ -0,0 +1,103 @@
+
+
+
+
+ ALTER RULE
+ 7
+ SQL - Language Statements
+
+
+
+ ALTER RULE
+ change the definition of a rule
+
+
+
+ ALTER RULE
+
+
+
+
+ALTER RULE name ON table_name RENAME TO new_name
+
+
+
+
+ Description
+
+
+ ALTER RULE changes properties of an existing
+ rule. The RENAME clause changes the name of
+ the given rule without otherwise changing the rule
+ definition.
+
+
+
+ You must own the table on which the rule acts to be allowed to change its properties.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name of an existing rule to alter.
+
+
+
+
+
+ table_name
+
+
+ The name of the table on which this rule acts.
+
+
+
+
+
+ new_name
+
+
+ The new name for the rule.
+
+
+
+
+
+
+
+ Examples
+
+
+ To rename an existing rule:
+
+ALTER RULE notify_all ON emp RENAME TO notify_me;
+
+
+
+
+ Compatibility
+
+
+ ALTER RULE is a
+ PostgreSQL language extension, as is the
+ entire query rewrite system.
+
+
+
+
+ See Also
+
+
+
+
+
+