From 6d0d15c451739396851d3f93f81c63a47535bf1e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 28 Sep 2002 20:00:19 +0000
Subject: [PATCH] Make the world at least somewhat safe for zero-column tables,
 and remove the special case in ALTER DROP COLUMN to prohibit dropping a
 table's last column.

---
 src/backend/access/common/tupdesc.c       | 65 ++++++++++++++---------
 src/backend/commands/tablecmds.c          | 29 ++--------
 src/backend/executor/execTuples.c         | 17 ++----
 src/backend/executor/nodeAgg.c            | 22 ++++----
 src/backend/parser/parse_target.c         |  6 ++-
 src/test/regress/expected/alter_table.out |  6 +--
 6 files changed, 64 insertions(+), 81 deletions(-)

diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 5a1b912b379..f1f92af4725 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.90 2002/09/22 19:42:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.91 2002/09/28 20:00:18 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -39,28 +39,34 @@
 TupleDesc
 CreateTemplateTupleDesc(int natts, bool hasoid)
 {
-	uint32		size;
 	TupleDesc	desc;
 
 	/*
 	 * sanity checks
 	 */
-	AssertArg(natts >= 1);
+	AssertArg(natts >= 0);
 
 	/*
 	 * allocate enough memory for the tuple descriptor and zero it as
 	 * TupleDescInitEntry assumes that the descriptor is filled with NULL
 	 * pointers.
 	 */
-	size = natts * sizeof(Form_pg_attribute);
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
-	desc->attrs = (Form_pg_attribute *) palloc(size);
-	desc->constr = NULL;
-	MemSet(desc->attrs, 0, size);
 
 	desc->natts = natts;
 	desc->tdhasoid = hasoid;
 
+	if (natts > 0)
+	{
+		uint32		size = natts * sizeof(Form_pg_attribute);
+
+		desc->attrs = (Form_pg_attribute *) palloc(size);
+		MemSet(desc->attrs, 0, size);
+	}
+	else
+		desc->attrs = NULL;
+	desc->constr = NULL;
+
 	return desc;
 }
 
@@ -79,7 +85,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
 	/*
 	 * sanity checks
 	 */
-	AssertArg(natts >= 1);
+	AssertArg(natts >= 0);
 
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 	desc->attrs = attrs;
@@ -108,17 +114,20 @@ CreateTupleDescCopy(TupleDesc tupdesc)
 
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 	desc->natts = tupdesc->natts;
-	size = desc->natts * sizeof(Form_pg_attribute);
-	desc->attrs = (Form_pg_attribute *) palloc(size);
-	for (i = 0; i < desc->natts; i++)
+	if (desc->natts > 0)
 	{
-		desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-		memcpy(desc->attrs[i],
-			   tupdesc->attrs[i],
-			   ATTRIBUTE_TUPLE_SIZE);
-		desc->attrs[i]->attnotnull = false;
-		desc->attrs[i]->atthasdef = false;
+		size = desc->natts * sizeof(Form_pg_attribute);
+		desc->attrs = (Form_pg_attribute *) palloc(size);
+		for (i = 0; i < desc->natts; i++)
+		{
+			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
+			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
+			desc->attrs[i]->attnotnull = false;
+			desc->attrs[i]->atthasdef = false;
+		}
 	}
+	else
+		desc->attrs = NULL;
 	desc->constr = NULL;
 	desc->tdhasoid = tupdesc->tdhasoid;
 
@@ -142,15 +151,18 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
 
 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
 	desc->natts = tupdesc->natts;
-	size = desc->natts * sizeof(Form_pg_attribute);
-	desc->attrs = (Form_pg_attribute *) palloc(size);
-	for (i = 0; i < desc->natts; i++)
+	if (desc->natts > 0)
 	{
-		desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-		memcpy(desc->attrs[i],
-			   tupdesc->attrs[i],
-			   ATTRIBUTE_TUPLE_SIZE);
+		size = desc->natts * sizeof(Form_pg_attribute);
+		desc->attrs = (Form_pg_attribute *) palloc(size);
+		for (i = 0; i < desc->natts; i++)
+		{
+			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
+			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
+		}
 	}
+	else
+		desc->attrs = NULL;
 	if (constr)
 	{
 		TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
@@ -197,7 +209,8 @@ FreeTupleDesc(TupleDesc tupdesc)
 
 	for (i = 0; i < tupdesc->natts; i++)
 		pfree(tupdesc->attrs[i]);
-	pfree(tupdesc->attrs);
+	if (tupdesc->attrs)
+		pfree(tupdesc->attrs);
 	if (tupdesc->constr)
 	{
 		if (tupdesc->constr->num_defval > 0)
@@ -228,7 +241,6 @@ FreeTupleDesc(TupleDesc tupdesc)
 	}
 
 	pfree(tupdesc);
-
 }
 
 /*
@@ -361,6 +373,7 @@ TupleDescInitEntry(TupleDesc desc,
 	 */
 	AssertArg(PointerIsValid(desc));
 	AssertArg(attributeNumber >= 1);
+	AssertArg(attributeNumber <= desc->natts);
 
 	/*
 	 * attributeName's are sometimes NULL, from resdom's.  I don't know
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0934a274c7a..14d82630c8a 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.44 2002/09/23 20:43:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.45 2002/09/28 20:00:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2331,9 +2331,7 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 {
 	Relation	rel;
 	AttrNumber	attnum;
-	AttrNumber	n;
 	TupleDesc	tupleDesc;
-	bool		success;
 	ObjectAddress object;
 
 	rel = heap_open(myrelid, AccessExclusiveLock);
@@ -2359,34 +2357,13 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 			 RelationGetRelationName(rel), colName);
 
 	/* Can't drop a system attribute */
+	/* XXX perhaps someday allow dropping OID? */
 	if (attnum < 0)
 		elog(ERROR, "ALTER TABLE: Cannot drop system attribute \"%s\"",
 			 colName);
 
-	/*
-	 * Make sure there will be at least one user column left in the
-	 * relation after we drop this one.  Zero-length tuples tend to
-	 * confuse us.
-	 */
-	tupleDesc = RelationGetDescr(rel);
-
-	success = false;
-	for (n = 1; n <= tupleDesc->natts; n++)
-	{
-		Form_pg_attribute attribute = tupleDesc->attrs[n - 1];
-
-		if (!attribute->attisdropped && n != attnum)
-		{
-			success = true;
-			break;
-		}
-	}
-
-	if (!success)
-		elog(ERROR, "ALTER TABLE: Cannot drop last column from table \"%s\"",
-			 RelationGetRelationName(rel));
-
 	/* Don't drop inherited columns */
+	tupleDesc = RelationGetDescr(rel);
 	if (tupleDesc->attrs[attnum - 1]->attinhcount > 0 && !recursing)
 		elog(ERROR, "ALTER TABLE: Cannot drop inherited column \"%s\"",
 			 colName);
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 1a5f835be1d..7b0df664c74 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.59 2002/09/04 20:31:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.60 2002/09/28 20:00:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -560,23 +560,14 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 TupleDesc
 ExecTypeFromTL(List *targetList, bool hasoid)
 {
-	List	   *tlitem;
 	TupleDesc	typeInfo;
-	Resdom	   *resdom;
-	Oid			restype;
+	List	   *tlitem;
 	int			len;
 
-	/*
-	 * examine targetlist - if empty then return NULL
-	 */
-	len = ExecTargetListLength(targetList);
-
-	if (len == 0)
-		return NULL;
-
 	/*
 	 * allocate a new typeInfo
 	 */
+	len = ExecTargetListLength(targetList);
 	typeInfo = CreateTemplateTupleDesc(len, hasoid);
 
 	/*
@@ -585,6 +576,8 @@ ExecTypeFromTL(List *targetList, bool hasoid)
 	foreach(tlitem, targetList)
 	{
 		TargetEntry *tle = lfirst(tlitem);
+		Resdom	   *resdom;
+		Oid			restype;
 
 		if (tle->resdom != NULL)
 		{
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 898ca62a600..0ebf2f7151e 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -46,7 +46,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.87 2002/09/18 21:35:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.88 2002/09/28 20:00:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -658,20 +658,20 @@ ExecAgg(Agg *node)
 			if (inputTuple == NULL)
 			{
 				TupleDesc	tupType;
-				Datum	   *tupValue;
-				char	   *null_array;
-				AttrNumber	attnum;
+				Datum	   *dvalues;
+				char	   *dnulls;
 
 				tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
-				tupValue = projInfo->pi_tupValue;
 				/* watch out for null input tuples, though... */
-				if (tupType && tupValue)
+				if (tupType && tupType->natts > 0)
 				{
-					null_array = (char *) palloc(sizeof(char) * tupType->natts);
-					for (attnum = 0; attnum < tupType->natts; attnum++)
-						null_array[attnum] = 'n';
-					inputTuple = heap_formtuple(tupType, tupValue, null_array);
-					pfree(null_array);
+					dvalues = (Datum *) palloc(sizeof(Datum) * tupType->natts);
+					dnulls = (char *) palloc(sizeof(char) * tupType->natts);
+					MemSet(dvalues, 0, sizeof(Datum) * tupType->natts);
+					MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
+					inputTuple = heap_formtuple(tupType, dvalues, dnulls);
+					pfree(dvalues);
+					pfree(dnulls);
 				}
 			}
 		}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 18d11cc7f5a..c03db4f8b4b 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.90 2002/09/18 21:35:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.91 2002/09/28 20:00:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -386,6 +386,7 @@ static List *
 ExpandAllTables(ParseState *pstate)
 {
 	List	   *target = NIL;
+	bool		found_table = false;
 	List	   *ns;
 
 	foreach(ns, pstate->p_namespace)
@@ -413,11 +414,12 @@ ExpandAllTables(ParseState *pstate)
 		if (!rte->inFromCl)
 			continue;
 
+		found_table = true;
 		target = nconc(target, expandRelAttrs(pstate, rte));
 	}
 
 	/* Check for SELECT *; */
-	if (target == NIL)
+	if (!found_table)
 		elog(ERROR, "Wildcard with no tables specified not allowed");
 
 	return target;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index de2e4a29757..f604c1c0dd0 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -939,11 +939,9 @@ drop table test2;
 alter table atacc1 drop c;
 alter table atacc1 drop d;
 alter table atacc1 drop b;
-ERROR:  ALTER TABLE: Cannot drop last column from table "atacc1"
 select * from atacc1;
- b  
-----
- 21
+  
+--
 (1 row)
 
 drop table atacc1;
-- 
GitLab