On 2017-Aug-02, Tom Lane wrote:

> I think Peter's got the error and the detail backwards.  It should be
> more like
> 
> ERROR: "someview" cannot have constraints
> DETAIL: "someview" is a view.
> 
> If we do it like that, we need one ERROR message per error reason,
> and one DETAIL per relkind, which should be manageable.

I support this idea.  Here's a proof-of-concept patch that corresponds
to one of the cases that Ashutosh was on about (specifically, the one
that uses the RELKIND_CAN_HAVE_STORAGE macro I just added).  If there
are no objections to this approach, I'm going to complete it along these
lines.

I put the new function at the bottom of heapam.c but I think it probably
needs a better place.

BTW are there other opinions on the RELKIND_HAS_STORAGE vs.
RELKIND_CAN_HAVE_STORAGE debate?  I'm inclined to change it to the
former.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
commit 399a6b2fdaffb7b5fb843af77bd4b046cfcce102
Author:     Alvaro Herrera <alvhe...@alvh.no-ip.org>
AuthorDate: Wed Dec 19 14:33:07 2018 -0300
CommitDate: Wed Dec 19 15:07:15 2018 -0300

    unsuitable relkind

diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index db396c8c4b..1f9eeac62b 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -757,13 +757,11 @@ GetHashPageStats(Page page, HashIndexStat *stats)
 static void
 check_relation_relkind(Relation rel)
 {
-	if (rel->rd_rel->relkind != RELKIND_RELATION &&
-		rel->rd_rel->relkind != RELKIND_INDEX &&
-		rel->rd_rel->relkind != RELKIND_MATVIEW &&
-		rel->rd_rel->relkind != RELKIND_SEQUENCE &&
-		rel->rd_rel->relkind != RELKIND_TOASTVALUE)
+	if (!RELKIND_CAN_HAVE_STORAGE(rel->rd_rel->relkind))
 		ereport(ERROR,
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table, index, materialized view, sequence, or TOAST table",
-						RelationGetRelationName(rel))));
+				 errmsg("cannot compute relpages of \"%s\"",
+						RelationGetRelationName(rel)),
+				 errdetail_unsuitable_relkind(rel->rd_rel->relkind,
+											  RelationGetRelationName(rel))));
 }
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 9650145642..fa2c99b367 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -9460,3 +9460,48 @@ heap_mask(char *pagedata, BlockNumber blkno)
 		}
 	}
 }
+
+/*
+ * Add an errdetail to the current error message indicating what the relkind of
+ * the relation is.  This is useful when an operation is being attempted on a
+ * relation that cannot accept it.
+ */
+int
+errdetail_unsuitable_relkind(char relkind, char *relname)
+{
+	switch (relkind)
+	{
+		case RELKIND_RELATION:
+			errdetail("%s is a plain table.", relname);
+			break;
+		case RELKIND_INDEX:
+			errdetail("%s is an index.", relname);
+			break;
+		case RELKIND_SEQUENCE:
+			errdetail("%s is a sequence.", relname);
+			break;
+		case RELKIND_TOASTVALUE:
+			errdetail("%s is a TOAST table.", relname);
+			break;
+		case RELKIND_VIEW:
+			errdetail("%s is a view.", relname);
+			break;
+		case RELKIND_MATVIEW:
+			errdetail("%s is a materialized view.", relname);
+			break;
+		case RELKIND_COMPOSITE_TYPE:
+			errdetail("%s is a composite type.", relname);
+			break;
+		case RELKIND_FOREIGN_TABLE:
+			errdetail("%s is a foreign table.", relname);
+			break;
+		case RELKIND_PARTITIONED_TABLE:
+			errdetail("%s is a partitioned table.", relname);
+			break;
+		case RELKIND_PARTITIONED_INDEX:
+			errdetail("%s is a partitioned index.", relname);
+			break;
+	}
+
+	return 0;
+}
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 64cfdbd2f0..c1679ea2f9 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -184,6 +184,8 @@ extern void simple_heap_update(Relation relation, ItemPointer otid,
 extern void heap_sync(Relation relation);
 extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot);
 
+extern int errdetail_unsuitable_relkind(char relkind, char *relname);
+
 /* in heap/pruneheap.c */
 extern void heap_page_prune_opt(Relation relation, Buffer buffer);
 extern int heap_page_prune(Relation relation, Buffer buffer,

Reply via email to