Here's another attempt to reduce the number of places in the code that
need to be updated when adding a new relkind.  It adds a few macros --
RELKIND_HAS_STORAGE(), RELKIND_HAS_SYSTEM_ATTS(), and
RELKIND_HAS_SYSTEM_GENERATED_ATTNAMES() and uses them in place of more
ad-hoc tests for the same conditions -- and it rewords a few
problematic messages (some of which are already slightly inaccurate)
so that they won't need updating when we add a new relkind for foreign
tables.

Thoughts?  This is not a complete solution by any means, but I don't
think it makes anything worse (unless you hate the proposed wording, I
suppose) and it has the added benefit of simplifying the code in a few
places.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index f341a72..c2eeb9c 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -109,16 +109,10 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 	rel = relation_openrv(relrv, AccessShareLock);
 
 	/* Check that this relation has storage */
-	if (rel->rd_rel->relkind == RELKIND_VIEW)
+	if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot get raw page from view \"%s\"",
-						RelationGetRelationName(rel))));
-	if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
-		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("cannot get raw page from composite type \"%s\"",
-						RelationGetRelationName(rel))));
+				 errmsg("cannot get raw page from relation without storage")));
 
 	/*
 	 * Reject attempts to read non-local temporary relations; we would be
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 438e8b0..48e9d0a 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1174,7 +1174,7 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
 		case RELKIND_RELATION:
 			return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
 		default:
-			/* sequences, composite types and views are not supported */
+			/* other relation types are not supported */
 			return NULL;
 	}
 }
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8027d74..2a0cf0c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -262,34 +262,13 @@ heap_create(const char *relname,
 		errdetail("System catalog modifications are currently disallowed.")));
 
 	/*
-	 * Decide if we need storage or not, and handle a couple other special
-	 * cases for particular relkinds.
+	 * Decide if we need storage or not.  If not, force reltablespace to zero,
+	 * mostly just for cleanliness' sake.  We also force reltablespace to zero
+	 * for sequences, since we don't support moving them around.
 	 */
-	switch (relkind)
-	{
-		case RELKIND_VIEW:
-		case RELKIND_COMPOSITE_TYPE:
-			create_storage = false;
-
-			/*
-			 * Force reltablespace to zero if the relation has no physical
-			 * storage.  This is mainly just for cleanliness' sake.
-			 */
-			reltablespace = InvalidOid;
-			break;
-		case RELKIND_SEQUENCE:
-			create_storage = true;
-
-			/*
-			 * Force reltablespace to zero for sequences, since we don't
-			 * support moving them around into different tablespaces.
-			 */
-			reltablespace = InvalidOid;
-			break;
-		default:
-			create_storage = true;
-			break;
-	}
+	create_storage = RELKIND_HAS_STORAGE(relkind);
+	if (!create_storage || relkind == RELKIND_SEQUENCE)
+		reltablespace = InvalidOid;
 
 	/*
 	 * Never allow a pg_class entry to explicitly specify the database's
@@ -390,7 +369,7 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
 	 * Skip this for a view or type relation, since those don't have system
 	 * attributes.
 	 */
-	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
+	if (RELKIND_HAS_SYSTEM_ATTS(relkind))
 	{
 		for (i = 0; i < natts; i++)
 		{
@@ -613,11 +592,11 @@ AddNewAttributeTuples(Oid new_rel_oid,
 	}
 
 	/*
-	 * Next we add the system attributes.  Skip OID if rel has no OIDs. Skip
-	 * all for a view or type relation.  We don't bother with making datatype
-	 * dependencies here, since presumably all these types are pinned.
+	 * Next we add the system attributes, if they're needed for this relkind.
+	 * We don't bother with making datatype dependencies here, since presumably
+	 * all these types are pinned.
 	 */
-	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
+	if (RELKIND_HAS_SYSTEM_ATTS(relkind))
 	{
 		for (i = 0; i < (int) lengthof(SysAtt); i++)
 		{
@@ -1592,11 +1571,8 @@ heap_drop_with_catalog(Oid relid)
 	/*
 	 * Schedule unlinking of the relation's physical files at commit.
 	 */
-	if (rel->rd_rel->relkind != RELKIND_VIEW &&
-		rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
-	{
+	if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
 		RelationDropStorage(rel);
-	}
 
 	/*
 	 * Close relcache entry, but *keep* AccessExclusiveLock on the relation
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 15464c3..94b6b0d 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -187,7 +187,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
 		/* No need for a WARNING if we already complained during VACUUM */
 		if (!(vacstmt->options & VACOPT_VACUUM))
 			ereport(WARNING,
-					(errmsg("skipping \"%s\" --- cannot analyze indexes, views, or special system tables",
+					(errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
 							RelationGetRelationName(onerel))));
 		relation_close(onerel, ShareUpdateExclusiveLock);
 		return;
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index b578818..aa8345a 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -574,19 +574,17 @@ CheckAttributeComment(Relation relation)
 					   RelationGetRelationName(relation));
 
 	/*
-	 * Allow comments only on columns of tables, views, and composite types
-	 * (which are the only relkinds for which pg_dump will dump per-column
-	 * comments).  In particular we wish to disallow comments on index
-	 * columns, because the naming of an index's columns may change across PG
-	 * versions, so dumping per-column comments could create reload failures.
+	 * Allow comments only on columns of relations for which the user controls
+	 * the attribute names (which should correspond to the relkinds for which
+	 * pg_dump will dump per-column comments).  In particular we wish to
+	 * disallow comments on index columns, because the naming of an index's
+	 * columns may change across PG versions, so dumping per-column comments
+	 * could create reload failures.
 	 */
-	if (relation->rd_rel->relkind != RELKIND_RELATION &&
-		relation->rd_rel->relkind != RELKIND_VIEW &&
-		relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+	if (RELKIND_HAS_SYSTEM_GENERATED_ATTNAMES(relation->rd_rel->relkind))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table, view, or composite type",
-						RelationGetRelationName(relation))));
+				 errmsg("comments on system-generated column names are not supported")));
 }
 
 /*
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 7b8bee8..6ad450d 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1227,25 +1227,14 @@ DoCopyTo(CopyState cstate)
 
 	if (cstate->rel)
 	{
-		if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
-		{
-			if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("cannot copy from view \"%s\"",
-								RelationGetRelationName(cstate->rel)),
-						 errhint("Try the COPY (SELECT ...) TO variant.")));
-			else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("cannot copy from sequence \"%s\"",
-								RelationGetRelationName(cstate->rel))));
-			else
-				ereport(ERROR,
-						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-						 errmsg("cannot copy from non-table relation \"%s\"",
-								RelationGetRelationName(cstate->rel))));
-		}
+		char	relkind = cstate->rel->rd_rel->relkind;
+
+		if (relkind != RELKIND_RELATION)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("cannot copy from non-table relation"),
+					 relkind == RELKIND_VIEW ?
+						errhint("Try the COPY (SELECT ...) TO variant.") : 0));
 	}
 
 	if (pipe)
@@ -1702,23 +1691,9 @@ CopyFrom(CopyState cstate)
 	Assert(cstate->rel);
 
 	if (cstate->rel->rd_rel->relkind != RELKIND_RELATION)
-	{
-		if (cstate->rel->rd_rel->relkind == RELKIND_VIEW)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("cannot copy to view \"%s\"",
-							RelationGetRelationName(cstate->rel))));
-		else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE)
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("cannot copy to sequence \"%s\"",
-							RelationGetRelationName(cstate->rel))));
-		else
-			ereport(ERROR,
-					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-					 errmsg("cannot copy to non-table relation \"%s\"",
-							RelationGetRelationName(cstate->rel))));
-	}
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot copy to non-table relation")));
 
 	/*----------
 	 * Check to see if we can avoid writing WAL
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 762bbae..9dc33c4 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -360,16 +360,14 @@ CheckAttributeSecLabel(Relation relation)
 					   RelationGetRelationName(relation));
 
 	/*
-	 * Allow security labels only on columns of tables, views, and composite
-	 * types (which are the only relkinds for which pg_dump will dump labels).
+	 * Allow security labels only on columns of relations for which the user
+	 * controls the attribute names (which should be the same relkinds for
+	 * which pg_dump will dump labels).
 	 */
-	if (relation->rd_rel->relkind != RELKIND_RELATION &&
-		relation->rd_rel->relkind != RELKIND_VIEW &&
-		relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+	if (RELKIND_HAS_SYSTEM_GENERATED_ATTNAMES(relation->rd_rel->relkind))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table, view, or composite type",
-						RelationGetRelationName(relation))));
+				 errmsg("security labels on system-generated column names are not supported")));
 }
 
 void
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3f6b814..e60b164 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1986,16 +1986,17 @@ renameatt_internal(Oid myrelid,
 	 * references are by attnum.  But it doesn't seem right to allow users to
 	 * change names that are hardcoded into the system, hence the following
 	 * restriction.
+	 *
+	 * NB: Index column names are also system-generated, and we elsewhere treat
+	 * them as such (e.g. comments and security labels on index columns are
+	 * refused), but we overlook that detail here for historical reasons.
 	 */
 	relkind = RelationGetForm(targetrelation)->relkind;
-	if (relkind != RELKIND_RELATION &&
-		relkind != RELKIND_VIEW &&
-		relkind != RELKIND_COMPOSITE_TYPE &&
-		relkind != RELKIND_INDEX)
+	if (RELKIND_HAS_SYSTEM_GENERATED_ATTNAMES(relkind)
+		&& relkind != RELKIND_INDEX)
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-			   errmsg("\"%s\" is not a table, view, composite type or index",
-					  RelationGetRelationName(targetrelation))));
+				 errmsg("system-generated column names may not be changed")));
 
 	/*
 	 * permissions checking.  only the owner of a class can change its schema.
@@ -4137,12 +4138,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	 * returned by AddRelationNewConstraints, so that the right thing happens
 	 * when a datatype's default applies.
 	 *
-	 * 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.
+	 * We skip this step completely for relations which do not have storage,
+	 * since phase 3 must not be invoked on such relations.
 	 */
-	if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE && attribute.attnum > 0)
+	if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
 	{
 		defval = (Expr *) build_column_default(rel, attribute.attnum);
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 2f68df4..028b36b 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -894,7 +894,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
 		onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
 	{
 		ereport(WARNING,
-				(errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables",
+				(errmsg("skipping \"%s\" --- cannot vacuum non-tables or special system tables",
 						RelationGetRelationName(onerel))));
 		relation_close(onerel, lmode);
 		PopActiveSnapshot();
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 856daa7..9db81e1 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1492,10 +1492,7 @@ pgstat_initstats(Relation rel)
 	char		relkind = rel->rd_rel->relkind;
 
 	/* We only count stats for things that have storage */
-	if (!(relkind == RELKIND_RELATION ||
-		  relkind == RELKIND_INDEX ||
-		  relkind == RELKIND_TOASTVALUE ||
-		  relkind == RELKIND_SEQUENCE))
+	if (!RELKIND_HAS_STORAGE(relkind))
 	{
 		rel->pgstat_info = NULL;
 		return;
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index f33c29e..e43c336 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -528,24 +528,18 @@ pg_relation_filenode(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	relform = (Form_pg_class) GETSTRUCT(tuple);
 
-	switch (relform->relkind)
+	if (RELKIND_HAS_STORAGE(relform->relkind))
 	{
-		case RELKIND_RELATION:
-		case RELKIND_INDEX:
-		case RELKIND_SEQUENCE:
-		case RELKIND_TOASTVALUE:
-			/* okay, these have storage */
-			if (relform->relfilenode)
-				result = relform->relfilenode;
-			else	/* Consult the relation mapper */
-				result = RelationMapOidToFilenode(relid,
-												  relform->relisshared);
-			break;
-
-		default:
-			/* no storage, return NULL */
-			result = InvalidOid;
-			break;
+		if (relform->relfilenode)
+			result = relform->relfilenode;
+		else	/* Consult the relation mapper */
+			result = RelationMapOidToFilenode(relid,
+											  relform->relisshared);
+	}
+	else
+	{
+		/* no storage, return NULL */
+		result = InvalidOid;
 	}
 
 	ReleaseSysCache(tuple);
@@ -576,34 +570,27 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 		PG_RETURN_NULL();
 	relform = (Form_pg_class) GETSTRUCT(tuple);
 
-	switch (relform->relkind)
+	if (RELKIND_HAS_STORAGE(relform->relkind))
 	{
-		case RELKIND_RELATION:
-		case RELKIND_INDEX:
-		case RELKIND_SEQUENCE:
-		case RELKIND_TOASTVALUE:
-			/* okay, these have storage */
-
-			/* This logic should match RelationInitPhysicalAddr */
-			if (relform->reltablespace)
-				rnode.spcNode = relform->reltablespace;
-			else
-				rnode.spcNode = MyDatabaseTableSpace;
-			if (rnode.spcNode == GLOBALTABLESPACE_OID)
-				rnode.dbNode = InvalidOid;
-			else
-				rnode.dbNode = MyDatabaseId;
-			if (relform->relfilenode)
-				rnode.relNode = relform->relfilenode;
-			else	/* Consult the relation mapper */
-				rnode.relNode = RelationMapOidToFilenode(relid,
-													   relform->relisshared);
-			break;
-
-		default:
-			/* no storage, return NULL */
-			rnode.relNode = InvalidOid;
-			break;
+		/* This logic should match RelationInitPhysicalAddr */
+		if (relform->reltablespace)
+			rnode.spcNode = relform->reltablespace;
+		else
+			rnode.spcNode = MyDatabaseTableSpace;
+		if (rnode.spcNode == GLOBALTABLESPACE_OID)
+			rnode.dbNode = InvalidOid;
+		else
+			rnode.dbNode = MyDatabaseId;
+		if (relform->relfilenode)
+			rnode.relNode = relform->relfilenode;
+		else	/* Consult the relation mapper */
+			rnode.relNode = RelationMapOidToFilenode(relid,
+												   relform->relisshared);
+	}
+	else
+	{
+		/* no storage, return NULL */
+		rnode.relNode = InvalidOid;
 	}
 
 	if (!OidIsValid(rnode.relNode))
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 39f9743..eb46c83 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -149,6 +149,14 @@ DESCR("");
 #define		  RELKIND_VIEW			  'v'		/* view */
 #define		  RELKIND_COMPOSITE_TYPE  'c'		/* composite type */
 
+#define		  RELKIND_HAS_STORAGE(c) \
+		((c) != RELKIND_VIEW && (c) != RELKIND_COMPOSITE_TYPE)
+#define		  RELKIND_HAS_SYSTEM_ATTS(c) \
+		((c) != RELKIND_VIEW && (c) != RELKIND_COMPOSITE_TYPE)
+#define		  RELKIND_HAS_SYSTEM_GENERATED_ATTNAMES(c) \
+		((c) != RELKIND_RELATION && (c) != RELKIND_VIEW && \
+		 (c) != RELKIND_COMPOSITE_TYPE)
+
 #define		  RELPERSISTENCE_PERMANENT	'p'
 #define		  RELPERSISTENCE_UNLOGGED	'u'
 #define		  RELPERSISTENCE_TEMP		't'
diff --git a/src/test/regress/expected/copyselect.out b/src/test/regress/expected/copyselect.out
index cbc140c..a98de6c 100644
--- a/src/test/regress/expected/copyselect.out
+++ b/src/test/regress/expected/copyselect.out
@@ -30,7 +30,7 @@ copy test1 to stdout;
 -- This should fail
 --
 copy v_test1 to stdout;
-ERROR:  cannot copy from view "v_test1"
+ERROR:  cannot copy from non-table relation
 HINT:  Try the COPY (SELECT ...) TO variant.
 --
 -- Test COPY (select) TO
@@ -109,9 +109,9 @@ t
 -- This should fail
 --
 \copy v_test1 to stdout
-ERROR:  cannot copy from view "v_test1"
+ERROR:  cannot copy from non-table relation
 HINT:  Try the COPY (SELECT ...) TO variant.
-\copy: ERROR:  cannot copy from view "v_test1"
+\copy: ERROR:  cannot copy from non-table relation
 HINT:  Try the COPY (SELECT ...) TO variant.
 --
 -- Test \copy (select ...)
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to