From 260faf0b633716d02e30103bd5e94eb40f06bf27 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 16 Dec 2002 18:39:22 +0000
Subject: [PATCH] Fix ALTER TABLE ADD COLUMN to disallow the same column types
 that are disallowed by CREATE TABLE (eg, pseudo-types); also disallow these
 types from being introduced by the range-function syntax.  While at it, allow
 CREATE TABLE to create zero-column tables, per recent pghackers discussion. I
 am back-patching this into 7.3 since failure to disallow pseudo-types is
 arguably a security hole.

---
 src/backend/catalog/heap.c         | 83 ++++++++++++++++++------------
 src/backend/commands/tablecmds.c   | 10 ++--
 src/backend/parser/parse_clause.c  | 15 +++++-
 src/backend/utils/cache/relcache.c |  7 +--
 src/include/catalog/heap.h         |  6 ++-
 5 files changed, 76 insertions(+), 45 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 424684d74d7..3d671e92f3d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.237 2002/12/12 20:35:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.238 2002/12/16 18:39:22 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -307,8 +307,8 @@ heap_storage_create(Relation rel)
  *
  *		this is done in 6 steps:
  *
- *		1) CheckAttributeNames() is used to make certain the tuple
- *		   descriptor contains a valid set of attribute names
+ *		1) CheckAttributeNamesTypes() is used to make certain the tuple
+ *		   descriptor contains a valid set of attribute names and types
  *
  *		2) pg_class is opened and get_relname_relid()
  *		   performs a scan to ensure that no relation with the
@@ -334,20 +334,25 @@ heap_storage_create(Relation rel)
  */
 
 /* --------------------------------
- *		CheckAttributeNames
+ *		CheckAttributeNamesTypes
  *
  *		this is used to make certain the tuple descriptor contains a
- *		valid set of attribute names.  a problem simply generates
- *		elog(ERROR) which aborts the current transaction.
+ *		valid set of attribute names and datatypes.  a problem simply
+ *		generates elog(ERROR) which aborts the current transaction.
  * --------------------------------
  */
-static void
-CheckAttributeNames(TupleDesc tupdesc, char relkind)
+void
+CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind)
 {
 	int			i;
 	int			j;
 	int			natts = tupdesc->natts;
 
+	/* Sanity check on column count */
+	if (natts < 0 || natts > MaxHeapAttributeNumber)
+		elog(ERROR, "Number of columns is out of range (0 to %d)",
+			 MaxHeapAttributeNumber);
+
 	/*
 	 * first check for collision with system attribute names
 	 *
@@ -380,8 +385,29 @@ CheckAttributeNames(TupleDesc tupdesc, char relkind)
 	}
 
 	/*
-	 * We also do some checking of the attribute types here.
-	 *
+	 * next check the attribute types
+	 */
+	for (i = 0; i < natts; i++)
+	{
+		CheckAttributeType(NameStr(tupdesc->attrs[i]->attname),
+						   tupdesc->attrs[i]->atttypid);
+	}
+}
+
+/* --------------------------------
+ *		CheckAttributeType
+ *
+ *		Verify that the proposed datatype of an attribute is legal.
+ *		This is needed because there are types (and pseudo-types)
+ *		in the catalogs that we do not support as elements of real tuples.
+ * --------------------------------
+ */
+void
+CheckAttributeType(const char *attname, Oid atttypid)
+{
+	char		att_typtype = get_typtype(atttypid);
+
+	/*
 	 * Warn user, but don't fail, if column to be created has UNKNOWN type
 	 * (usually as a result of a 'retrieve into' - jolly)
 	 *
@@ -390,28 +416,20 @@ CheckAttributeNames(TupleDesc tupdesc, char relkind)
 	 * all references to complex types, but for now there's still some
 	 * Berkeley-derived code that thinks it can do this...)
 	 */
-	for (i = 0; i < natts; i++)
+	if (atttypid == UNKNOWNOID)
+		elog(WARNING, "Attribute \"%s\" has an unknown type"
+			 "\n\tProceeding with relation creation anyway",
+			 attname);
+	else if (att_typtype == 'p')
+		elog(ERROR, "Attribute \"%s\" has pseudo-type %s",
+			 attname, format_type_be(atttypid));
+	else if (att_typtype == 'c')
 	{
-		Oid			att_type = tupdesc->attrs[i]->atttypid;
-		char		att_typtype = get_typtype(att_type);
-
-		if (att_type == UNKNOWNOID)
-			elog(WARNING, "Attribute \"%s\" has an unknown type"
-				 "\n\tProceeding with relation creation anyway",
-				 NameStr(tupdesc->attrs[i]->attname));
-		if (att_typtype == 'p')
-			elog(ERROR, "Attribute \"%s\" has pseudo-type %s",
-				 NameStr(tupdesc->attrs[i]->attname),
-				 format_type_be(att_type));
-		if (att_typtype == 'c')
-		{
-			Oid		typrelid = get_typ_typrelid(att_type);
+		Oid		typrelid = get_typ_typrelid(atttypid);
 
-			if (get_rel_relkind(typrelid) == RELKIND_COMPOSITE_TYPE)
-				elog(ERROR, "Attribute \"%s\" has composite type %s",
-					 NameStr(tupdesc->attrs[i]->attname),
-					 format_type_be(att_type));
-		}
+		if (get_rel_relkind(typrelid) == RELKIND_COMPOSITE_TYPE)
+			elog(ERROR, "Attribute \"%s\" has composite type %s",
+				 attname, format_type_be(atttypid));
 	}
 }
 
@@ -689,11 +707,8 @@ heap_create_with_catalog(const char *relname,
 	 * sanity checks
 	 */
 	Assert(IsNormalProcessingMode() || IsBootstrapProcessingMode());
-	if (tupdesc->natts <= 0 || tupdesc->natts > MaxHeapAttributeNumber)
-		elog(ERROR, "Number of columns is out of range (1 to %d)",
-			 MaxHeapAttributeNumber);
 
-	CheckAttributeNames(tupdesc, relkind);
+	CheckAttributeNamesTypes(tupdesc, relkind);
 
 	if (get_relname_relid(relname, relnamespace))
 		elog(ERROR, "Relation '%s' already exists", relname);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 09c60bdf3f9..2a1a2b4cf49 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.61 2002/12/15 16:17:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.62 2002/12/16 18:39:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -125,7 +125,6 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	char		relname[NAMEDATALEN];
 	Oid			namespaceId;
 	List	   *schema = stmt->tableElts;
-	int			numberOfAttributes;
 	Oid			relationId;
 	Relation	rel;
 	TupleDesc	descriptor;
@@ -174,10 +173,6 @@ DefineRelation(CreateStmt *stmt, char relkind)
 							 stmt->relation->istemp,
 						 &inheritOids, &old_constraints, &parentHasOids);
 
-	numberOfAttributes = length(schema);
-	if (numberOfAttributes <= 0)
-		elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
-
 	/*
 	 * Create a relation descriptor from the relation schema and create
 	 * the relation.  Note that in this stage only inherited (pre-cooked)
@@ -1800,6 +1795,9 @@ AlterTableAddColumn(Oid myrelid,
 	typeTuple = typenameType(colDef->typename);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 
+	/* make sure datatype is legal for a column */
+	CheckAttributeType(colDef->colname, HeapTupleGetOid(typeTuple));
+
 	attributeTuple = heap_addheader(Natts_pg_attribute,
 									false,
 									ATTRIBUTE_TUPLE_SIZE,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 71375f62c97..5d729216751 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.102 2002/12/12 20:35:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.103 2002/12/16 18:39:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "catalog/heap.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
@@ -502,6 +503,18 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 			elog(ERROR, "cannot use aggregate function in FROM function expression");
 	}
 
+	/*
+	 * If a coldeflist is supplied, ensure it defines a legal set of names
+	 * (no duplicates) and datatypes (no pseudo-types, for instance).
+	 */
+	if (r->coldeflist)
+	{
+		TupleDesc	tupdesc;
+
+		tupdesc = BuildDescForRelation(r->coldeflist);
+		CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE);
+	}
+
 	/*
 	 * OK, build an RTE for the function.
 	 */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 87992769e99..15f61ce59a6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.182 2002/12/15 21:01:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.183 2002/12/16 18:39:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -570,7 +570,8 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 	 * cases for attnum=1 that used to exist in fastgetattr() and
 	 * index_getattr().
 	 */
-	relation->rd_att->attrs[0]->attcacheoff = 0;
+	if (relation->rd_rel->relnatts > 0)
+		relation->rd_att->attrs[0]->attcacheoff = 0;
 
 	/*
 	 * Set up constraint/default info
@@ -2049,7 +2050,7 @@ RelationBuildLocalRelation(const char *relname,
 	int			i;
 	bool		has_not_null;
 
-	AssertArg(natts > 0);
+	AssertArg(natts >= 0);
 
 	/*
 	 * switch to the cache context to create the relcache entry.
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 677347c24f5..1a69f0147ff 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.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: heap.h,v 1.59 2002/11/11 22:19:23 tgl Exp $
+ * $Id: heap.h,v 1.60 2002/12/16 18:39:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -74,4 +74,8 @@ extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
 extern Form_pg_attribute SystemAttributeByName(const char *attname,
 					  bool relhasoids);
 
+extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind);
+
+extern void CheckAttributeType(const char *attname, Oid atttypid);
+
 #endif   /* HEAP_H */
-- 
GitLab