diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 952496fc1df9986bc169763c4dd6e3b55370bcee..e8b0d8bc013b3b527394e080a70069e220064d3c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.8 2002/04/24 02:38:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.9 2002/04/24 02:48:54 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2846,6 +2846,123 @@ renamerel(Oid relid, const char *newrelname)
 	relation_close(targetrelation, NoLock);
 }
 
+/*
+ *		renametrig		- changes the name of a trigger on a relation
+ *
+ *		trigger name is changed in trigger catalog.
+ *		No record of the previous name is kept.
+ *
+ *		get proper relrelation from relation catalog (if not arg)
+ *		scan trigger catalog
+ *				for name conflict (within rel)
+ *				for original trigger (if not arg)
+ *		modify tgname in trigger tuple
+ *		insert modified trigger in trigger catalog
+ *		delete original trigger from trigger catalog
+ */
+extern void renametrig(Oid relid,
+		  const char *oldname,
+		  const char *newname)
+{
+	Relation	targetrel;
+	Relation	tgrel;
+	HeapTuple	tuple;
+	SysScanDesc	tgscan;
+	ScanKeyData key;
+	bool		found = FALSE;
+	Relation	idescs[Num_pg_trigger_indices];
+
+	/*
+	 * Grab an exclusive lock on the target table, which we will NOT
+	 * release until end of transaction.
+	 */
+	targetrel = heap_open(relid, AccessExclusiveLock);
+
+	/*
+	 * Scan pg_trigger twice for existing triggers on relation.  We do this in
+	 * order to ensure a trigger does not exist with newname (The unique index
+	 * on tgrelid/tgname would complain anyway) and to ensure a trigger does
+	 * exist with oldname.
+	 *
+	 * NOTE that this is cool only because we have AccessExclusiveLock on the
+	 * relation, so the trigger set won't be changing underneath us.
+	 */
+	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+
+	/*
+	 * First pass -- look for name conflict
+	 */
+	ScanKeyEntryInitialize(&key, 0,
+						   Anum_pg_trigger_tgrelid,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(relid));
+	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
+								SnapshotNow, 1, &key);
+	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+	{
+		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+
+		if (namestrcmp(&(pg_trigger->tgname), newname) == 0)
+			elog(ERROR, "renametrig: trigger %s already defined on relation %s",
+				 newname, RelationGetRelationName(targetrel));
+	}
+	systable_endscan(tgscan);
+
+	/*
+	 * Second pass -- look for trigger existing with oldname and update
+	 */
+	ScanKeyEntryInitialize(&key, 0,
+						   Anum_pg_trigger_tgrelid,
+						   F_OIDEQ,
+						   ObjectIdGetDatum(relid));
+	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
+								SnapshotNow, 1, &key);
+	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+	{
+		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+
+		if (namestrcmp(&(pg_trigger->tgname), oldname) == 0)
+		{
+			/*
+			 * Update pg_trigger tuple with new tgname.
+			 * (Scribbling on tuple is OK because it's a copy...)
+			 */
+			namestrcpy(&(pg_trigger->tgname), newname);
+			simple_heap_update(tgrel, &tuple->t_self, tuple);
+
+			/*
+			 * keep system catalog indices current
+			 */
+			CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
+			CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
+			CatalogCloseIndices(Num_pg_trigger_indices, idescs);
+
+			/*
+			 * Invalidate relation's relcache entry so that other
+			 * backends (and this one too!) are sent SI message to make them
+			 * rebuild relcache entries.
+			 */
+			CacheInvalidateRelcache(relid);
+
+			found = TRUE;
+			break;
+		}
+	}
+	systable_endscan(tgscan);
+
+	heap_close(tgrel, RowExclusiveLock);
+
+	if (!found)
+		elog(ERROR, "renametrig: trigger %s not defined on relation %s",
+			 oldname, RelationGetRelationName(targetrel));
+
+	/*
+	 * Close rel, but keep exclusive lock!
+	 */
+	heap_close(targetrel, NoLock);
+}
+
+
 /*
  * Given a trigger function OID, determine whether it is an RI trigger,
  * and if so whether it is attached to PK or FK relation.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d9d5d13d8d46ef89c4639ccf8efe11ae404c6bde..ac25cd522956a0a34939aed7f362cff2947c72a8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.180 2002/04/18 20:01:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.181 2002/04/24 02:48:54 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2137,10 +2137,11 @@ _copyRenameStmt(RenameStmt *from)
 	RenameStmt *newnode = makeNode(RenameStmt);
 
 	Node_Copy(from, newnode, relation);
-	if (from->column)
-		newnode->column = pstrdup(from->column);
+	if (from->oldname)
+		newnode->oldname = pstrdup(from->oldname);
 	if (from->newname)
 		newnode->newname = pstrdup(from->newname);
+	newnode->renameType = from->renameType;
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 42030b272684e54c4628ae3ae41f0cb392f6b8a9..3deb6207c3e43927732ddbbfcfa177a3dd80bfc0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.128 2002/04/18 20:01:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.129 2002/04/24 02:48:54 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -983,10 +983,12 @@ _equalRenameStmt(RenameStmt *a, RenameStmt *b)
 {
 	if (!equal(a->relation, b->relation))
 		return false;
-	if (!equalstr(a->column, b->column))
+	if (!equalstr(a->oldname, b->oldname))
 		return false;
 	if (!equalstr(a->newname, b->newname))
 		return false;
+	if (a->renameType != b->renameType)
+		return false;
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a7cae5b116fec5002b308b9963546e347439c408..6e768c5bc4196a268d3f266b81b7f93b504c49b1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.309 2002/04/21 21:53:23 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.310 2002/04/24 02:48:54 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2919,8 +2919,21 @@ RenameStmt:  ALTER TABLE relation_expr RENAME opt_column opt_name TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
 					n->relation = $3;
-					n->column = $6;
+					n->oldname = $6;
 					n->newname = $8;
+					if ($6 == NULL)
+						n->renameType = RENAME_TABLE;
+					else
+						n->renameType = RENAME_COLUMN;
+					$$ = (Node *)n;
+				}
+		| ALTER TRIGGER name ON relation_expr RENAME TO name
+				{
+					RenameStmt *n = makeNode(RenameStmt);
+					n->relation = $5;
+					n->oldname = $3;
+					n->newname = $8;
+					n->renameType = RENAME_TRIGGER;
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 24a1e3d2964133cfa0bacca89f22e531cfdb7b75..d1d670228ffe3b22bc674f7d7afb83e18bfacb70 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.150 2002/04/18 20:01:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.151 2002/04/24 02:48:55 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -377,23 +377,30 @@ ProcessUtility(Node *parsetree,
 
 				CheckOwnership(stmt->relation, true);
 
-				if (stmt->column == NULL)
+				switch (stmt->renameType)
 				{
-					/*
-					 * rename relation
-					 */
-					renamerel(RangeVarGetRelid(stmt->relation, false),
-							  stmt->newname);
-				}
-				else
-				{
-					/*
-					 * rename attribute
-					 */
-					renameatt(RangeVarGetRelid(stmt->relation, false),
-							  stmt->column,		/* old att name */
+					case RENAME_TABLE:
+						renamerel(RangeVarGetRelid(stmt->relation, false),
+								  stmt->newname);
+						break;
+					case RENAME_COLUMN:
+						renameatt(RangeVarGetRelid(stmt->relation, false),
+							  stmt->oldname,	/* old att name */
 							  stmt->newname,	/* new att name */
-							  interpretInhOption(stmt->relation->inhOpt));		/* recursive? */
+							  interpretInhOption(stmt->relation->inhOpt));	/* recursive? */
+						break;
+					case RENAME_TRIGGER:
+						renametrig(RangeVarGetRelid(stmt->relation, false),
+							  stmt->oldname,	/* old att name */
+							  stmt->newname);	/* new att name */
+						break;
+					case RENAME_RULE:
+						elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
+								stmt->renameType);
+						break;
+					default:
+						elog(ERROR, "ProcessUtility: Invalid target for RENAME: %d",
+								stmt->renameType);
 				}
 			}
 			break;
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 5d895972f5b069fbabcbf521ff366a594ecdb906..f3dfcd6b9b1af0c6c5afbb14725e36b6ceb618e1 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tablecmds.h,v 1.1 2002/04/15 05:22:04 tgl Exp $
+ * $Id: tablecmds.h,v 1.2 2002/04/24 02:48:55 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define TABLECMDS_H
 
 #include "nodes/parsenodes.h"
+#include "utils/inval.h"
 
 extern void AlterTableAddColumn(Oid myrelid, bool inherits,
 								ColumnDef *colDef);
@@ -60,4 +61,8 @@ extern void renameatt(Oid relid,
 extern void renamerel(Oid relid,
 		  const char *newrelname);
 
+extern void renametrig(Oid relid,
+		  const char *oldname,
+		  const char *newname);
+
 #endif   /* TABLECMDS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1433083b2fba9354832dad97df79de7b4563ebca..60703a06dccfd7c0897e85ba08048bbbd435f95e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.173 2002/04/21 00:26:43 tgl Exp $
+ * $Id: parsenodes.h,v 1.174 2002/04/24 02:48:55 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1233,17 +1233,23 @@ typedef struct RemoveOperStmt
 } RemoveOperStmt;
 
 /* ----------------------
- *		Alter Table Rename Statement
+ *		Alter Object Rename Statement
  * ----------------------
+ * Currently supports renaming tables, table columns, and triggers.
+ * If renaming a table, oldname is ignored.
  */
+#define RENAME_TABLE	110
+#define RENAME_COLUMN	111
+#define RENAME_TRIGGER	112
+#define RENAME_RULE		113
+
 typedef struct RenameStmt
 {
 	NodeTag		type;
-	RangeVar   *relation;		/* relation to be altered */
-	char	   *column;			/* if NULL, rename the relation name to
-								 * the new name. Otherwise, rename this
-								 * column name. */
+	RangeVar   *relation;		/* owning relation */
+	char	   *oldname;		/* name of rule, trigger, etc */
 	char	   *newname;		/* the new name */
+	int			renameType;		/* RENAME_TABLE, RENAME_COLUMN, etc */
 } RenameStmt;
 
 /* ----------------------