diff --git a/doc/src/sgml/ref/create_view.sgml b/doc/src/sgml/ref/create_view.sgml
index 7cbbdbe1ced77d66d9b855ca3e74d211c34c7b79..e0347a1919d324676aa58412308e8ab0df2fe94a 100644
--- a/doc/src/sgml/ref/create_view.sgml
+++ b/doc/src/sgml/ref/create_view.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.38 2008/12/06 23:22:46 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_view.sgml,v 1.39 2008/12/15 21:35:31 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -38,9 +38,10 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
   <para>
    <command>CREATE OR REPLACE VIEW</command> is similar, but if a view
    of the same name already exists, it is replaced.  The new query must
-   generate all of the same columns that were generated by the original query
-   in the same order and with the same data types, but may add additional
-   columns to the end of the list.
+   generate the same columns that were generated by the existing view query
+   (that is, the same column names in the same order and with the same data
+   types), but it may add additional columns to the end of the list.  The
+   calculations giving rise to the output columns may be completely different.
   </para>
 
   <para>
@@ -77,7 +78,7 @@ CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] VIEW <replaceable class="PARAMETER">n
      </para>
     </listitem>
    </varlistentry>
-   
+
    <varlistentry>
     <term><replaceable class="parameter">name</replaceable></term>
     <listitem>
@@ -164,7 +165,7 @@ CREATE VIEW comedies AS
 </programlisting>
   </para>
  </refsect1>
- 
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6c60ddd5c104f79909116f844a56df99bc7747bb..9f34c735028d54b0ef38ddeba301b7cb8768d6ef 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.273 2008/12/13 19:13:44 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.274 2008/12/15 21:35:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3459,9 +3459,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 				attrdesc;
 	HeapTuple	reltup;
 	FormData_pg_attribute attribute;
-	int			i;
-	int			minattnum,
-				maxatts;
+	int			newattnum;
 	char		relkind;
 	HeapTuple	typeTuple;
 	Oid			typeOid;
@@ -3520,6 +3518,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 								0, 0, 0);
 	if (!HeapTupleIsValid(reltup))
 		elog(ERROR, "cache lookup failed for relation %u", myrelid);
+	relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
 
 	/*
 	 * this test is deliberately not attisdropped-aware, since if one tries to
@@ -3534,15 +3533,12 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 				 errmsg("column \"%s\" of relation \"%s\" already exists",
 						colDef->colname, RelationGetRelationName(rel))));
 
-	minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
-	relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
-	maxatts = minattnum + 1;
-	if (maxatts > MaxHeapAttributeNumber)
+	newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
+	if (newattnum > MaxHeapAttributeNumber)
 		ereport(ERROR,
 				(errcode(ERRCODE_TOO_MANY_COLUMNS),
 				 errmsg("tables can have at most %d columns",
 						MaxHeapAttributeNumber)));
-	i = minattnum + 1;
 
 	typeTuple = typenameType(NULL, colDef->typename, &typmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -3551,6 +3547,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colDef->colname, typeOid);
 
+	/* construct new attribute's pg_attribute entry */
 	attribute.attrelid = myrelid;
 	namestrcpy(&(attribute.attname), colDef->colname);
 	attribute.atttypid = typeOid;
@@ -3558,7 +3555,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	attribute.attlen = tform->typlen;
 	attribute.attcacheoff = -1;
 	attribute.atttypmod = typmod;
-	attribute.attnum = i;
+	attribute.attnum = newattnum;
 	attribute.attbyval = tform->typbyval;
 	attribute.attndims = list_length(colDef->typename->arrayBounds);
 	attribute.attstorage = tform->typstorage;
@@ -3578,7 +3575,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	/*
 	 * Update number of attributes in pg_class tuple
 	 */
-	((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
+	((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
 
 	simple_heap_update(pgclass, &reltup->t_self, reltup);
 
@@ -3635,9 +3632,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	 * returned by AddRelationNewConstraints, so that the right thing happens
 	 * when a datatype's default applies.
 	 *
-	 * We skip this logic completely for views.
+	 * We skip this step completely for views.  For a view, we can only get
+	 * here from CREATE OR REPLACE VIEW, which historically doesn't set up
+	 * defaults, not even for domain-typed columns.  And in any case we mustn't
+	 * invoke Phase 3 on a view, since it has no storage.
 	 */
-	if (relkind != RELKIND_VIEW) {
+	if (relkind != RELKIND_VIEW)
+	{
 		defval = (Expr *) build_column_default(rel, attribute.attnum);
 
 		if (!defval && GetDomainConstraints(typeOid) != NIL)
@@ -3680,7 +3681,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	/*
 	 * Add needed dependency entries for the new column.
 	 */
-	add_column_datatype_dependency(myrelid, i, attribute.atttypid);
+	add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
 }
 
 /*
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 23a03d28a9ccc89215d1169353473f4ca9a5f058..3b589d49e813bfed683dc51a48558df78d4e988b 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.108 2008/12/06 23:22:46 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.109 2008/12/15 21:35:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -165,6 +165,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 						   RelationGetRelationName(rel));
 
+		/* Also check it's not in use already */
+		CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
+
 		/*
 		 * Due to the namespace visibility rules for temporary objects, we
 		 * should only end up replacing a temporary view with another
@@ -173,37 +176,41 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
 		Assert(relation->istemp == rel->rd_istemp);
 
 		/*
- 		 * If new attributes have been added, we must modify the pre-existing
- 		 * view.
- 		 */
-		if (list_length(attrList) > rel->rd_att->natts) {
+		 * Create a tuple descriptor to compare against the existing view, and
+		 * verify that the old column list is an initial prefix of the new
+		 * column list.
+		 */
+		descriptor = BuildDescForRelation(attrList);
+		checkViewTupleDesc(descriptor, rel->rd_att);
+
+		/*
+ 		 * If new attributes have been added, we must add pg_attribute entries
+		 * for them.  It is convenient (although overkill) to use the ALTER
+		 * TABLE ADD COLUMN infrastructure for this.
+		 */
+		if (list_length(attrList) > rel->rd_att->natts)
+		{
 			List		*atcmds = NIL;
 			ListCell 	*c;
 			int			skip = rel->rd_att->natts;
 
-			foreach(c, attrList) {
+			foreach(c, attrList)
+			{
 				AlterTableCmd *atcmd;
 
-				if (skip > 0) {
-					--skip;
+				if (skip > 0)
+				{
+					skip--;
 					continue;
 				}
 				atcmd = makeNode(AlterTableCmd);
 				atcmd->subtype = AT_AddColumnToView;
-				atcmd->def = lfirst(c);
+				atcmd->def = (Node *) lfirst(c);
 				atcmds = lappend(atcmds, atcmd);
 			}
 			AlterTableInternal(viewOid, atcmds, true);
 		}
 
-		/*
-		 * Create a tuple descriptor to compare against the existing view, and
-		 * verify that the old column list is an initial prefix of the new
-		 * column list.
-		 */
-		descriptor = BuildDescForRelation(attrList);
-		checkViewTupleDesc(descriptor, rel->rd_att);
-
 		/*
 		 * Seems okay, so return the OID of the pre-existing view.
 		 */
@@ -238,6 +245,7 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
  * Verify that tupledesc associated with proposed new view definition
  * matches tupledesc of old view.  This is basically a cut-down version
  * of equalTupleDescs(), with code added to generate specific complaints.
+ * Also, we allow the new tupledesc to have more columns than the old.
  */
 static void
 checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index e9b6c83e25d883affc095a332b2a83f5ce231029..90e6c70e572f716e419b3dff0b410d94aa84fd15 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -53,7 +53,7 @@ ERROR:  cannot drop columns from view
 -- should fail
 CREATE OR REPLACE VIEW viewtest AS
 	SELECT 1, * FROM viewtest_tbl;
-ERROR:  column "b" of relation "viewtest" already exists
+ERROR:  cannot change name of view column "a"
 -- should fail
 CREATE OR REPLACE VIEW viewtest AS
 	SELECT a, b::numeric FROM viewtest_tbl;