diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index b3fc57d..c61c62f
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -25,6 +25,7 @@ Complete list of usable sgml source file
 <!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
 <!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
 <!ENTITY alterRole          SYSTEM "alter_role.sgml">
+<!ENTITY alterRule          SYSTEM "alter_rule.sgml">
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
 <!ENTITY alterSequence      SYSTEM "alter_sequence.sgml">
diff --git a/doc/src/sgml/ref/create_rule.sgml b/doc/src/sgml/ref/create_rule.sgml
new file mode 100644
index 381ea3e..7e36aef
--- 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
    entire query rewrite system.
   </para>
  </refsect1>
+
+  <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterrule"></member>
+  </simplelist>
+ </refsect1>
 </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index fe90227..5b0c774
--- 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
new file mode 100644
index c2d4bb3..3cc6e9a
--- 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
new file mode 100644
index 342b796..73cb49d
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -7030,6 +7030,16 @@ RenameStmt: ALTER AGGREGATE func_name ag
 					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
new file mode 100644
index ac724c3..dbdad19
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -753,35 +753,69 @@ EnableDisableRule(Relation rel, const ch
 
 /*
  * 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 c
 
 	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
new file mode 100644
index 09396ca..8c4d32f
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -633,6 +633,15 @@ static const SchemaQuery Query_for_list_
 "       (SELECT tgrelid FROM pg_catalog.pg_trigger "\
 "         WHERE pg_catalog.quote_ident(tgname)='%s')"
 
+/* the silly-looking length condition is just to eat up the current word */
+#define Query_for_list_of_relations_for_rule \
+"SELECT pg_catalog.quote_ident(relname) "\
+"  FROM pg_catalog.pg_class"\
+" WHERE (%d = pg_catalog.length('%s'))"\
+"   AND oid IN "\
+"       (SELECT ev_class FROM pg_catalog.pg_rewrite "\
+"         WHERE pg_catalog.quote_ident(rulename)='%s')"
+
 #define Query_for_list_of_ts_configurations \
 "SELECT pg_catalog.quote_ident(cfgname) FROM pg_catalog.pg_ts_config "\
 " WHERE substring(pg_catalog.quote_ident(cfgname),1,%d)='%s'"
@@ -925,7 +934,7 @@ psql_completion(char *text, int start, i
 		{"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 +1690,25 @@ psql_completion(char *text, int start, i
 			 pg_strcasecmp(prev_wd, "USER") == 0)
 		COMPLETE_WITH_QUERY(Query_for_list_of_roles);
 
+	/* ALTER RULE <name> */
+	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev2_wd, "RULE") == 0)
+		COMPLETE_WITH_CONST("ON");
+
+	/* ALTER RULE <name> ON <name>*/
+	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev4_wd, "RULE") == 0)
+		COMPLETE_WITH_CONST("RENAME TO");
+
+	/* If we have ALTER RULE <name> 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)
+	{
+		completion_info_charp = prev2_wd;
+		COMPLETE_WITH_QUERY(Query_for_list_of_relations_for_rule);
+	}
+
 /* 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
new file mode 100644
index dda267b..72921c3
--- a/src/include/rewrite/rewriteDefine.h
+++ b/src/include/rewrite/rewriteDefine.h
@@ -32,7 +32,7 @@ extern Oid DefineQueryRewrite(char *rule
 				   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
new file mode 100644
index a235571..7e79815
--- 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
new file mode 100644
index 458c2f0..abdd763
--- 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 ...c98c35b
--- a/doc/src/sgml/ref/alter_rule.sgml
+++ b/doc/src/sgml/ref/alter_rule.sgml
@@ -0,0 +1,104 @@
+<!--
+doc/src/sgml/ref/alter_rule.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERRULE">
+ <refmeta>
+  <refentrytitle>ALTER RULE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER RULE</refname>
+  <refpurpose>change the definition of a rule</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterrule">
+  <primary>ALTER RULE</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER RULE <replaceable class="PARAMETER">name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER RULE</command> changes properties of an existing
+   rule.  The <literal>RENAME</literal> clause changes the name of
+   the given rule without otherwise changing the rule
+   definition.
+  </para>
+
+  <para>
+   You must own the table or view on which the rule acts to be allowed to
+   change its properties.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="PARAMETER">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing rule to alter.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">table_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the table or view on which this rule acts.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">new_name</replaceable></term>
+    <listitem>
+     <para>
+      The new name for the rule.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To rename an existing rule:
+<programlisting>
+ALTER RULE notify_all ON emp RENAME TO notify_me;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>ALTER RULE</command> is a
+   <productname>PostgreSQL</productname> language extension, as is the
+   entire query rewrite system.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createrule"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
