From 999b351022699b4af22194f63ff2fc00b4d79609 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 18 Apr 2018 19:46:42 +1000
Subject: [PATCH 15/15] External relation infrastructure

Extensions can create an external relation in pg_class and use
it for their internal functionality and these tables cannot be
queried directly from SQL commands.

These tables are not visible from psql \d commands and also these
are not dumped by the pg_dump. we may need to enhance pg_dump
to handle these external relations for pg_upgrade.
---
 contrib/pageinspect/rawpage.c          | 6 ++++++
 contrib/pgstattuple/pgstattuple.c      | 3 +++
 src/backend/access/common/reloptions.c | 1 +
 src/backend/catalog/heap.c             | 3 ++-
 src/backend/catalog/namespace.c        | 6 ++++++
 src/backend/catalog/objectaddress.c    | 7 +++++++
 src/backend/executor/execMain.c        | 6 ++++++
 src/backend/parser/parse_relation.c    | 6 ++++++
 src/backend/utils/adt/dbsize.c         | 2 ++
 src/backend/utils/cache/relcache.c     | 7 +++++--
 src/include/catalog/pg_class.h         | 1 +
 11 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 72f1d21e1b..0760d981c8 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -128,6 +128,12 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno)
 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 				 errmsg("cannot get raw page from partitioned table \"%s\"",
 						RelationGetRelationName(rel))));
+	if (rel->rd_rel->relkind == RELKIND_EXTERNAL)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot get raw page from external table \"%s\"",
+						RelationGetRelationName(rel))));
+
 
 	/*
 	 * Reject attempts to read non-local temporary relations; we would be
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index 6a625ad20a..a1754f389e 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -296,6 +296,9 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo)
 		case RELKIND_PARTITIONED_TABLE:
 			err = "partitioned table";
 			break;
+		case RELKIND_EXTERNAL:
+			err = "external table";
+			break;
 		default:
 			err = "unknown";
 			break;
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 69ab2f101c..75af4d140f 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1014,6 +1014,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 			options = index_reloptions(amoptions, datum, false);
 			break;
 		case RELKIND_FOREIGN_TABLE:
+		case RELKIND_EXTERNAL:
 			options = NULL;
 			break;
 		default:
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index ca9f040077..5755a34347 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1396,7 +1396,8 @@ heap_create_init_fork(Relation rel)
 {
 	Assert(rel->rd_rel->relkind == RELKIND_RELATION ||
 		   rel->rd_rel->relkind == RELKIND_MATVIEW ||
-		   rel->rd_rel->relkind == RELKIND_TOASTVALUE);
+		   rel->rd_rel->relkind == RELKIND_TOASTVALUE ||
+		   rel->rd_rel->relkind == RELKIND_EXTERNAL);
 	RelationOpenSmgr(rel);
 	smgrcreate(rel->rd_smgr, INIT_FORKNUM, false);
 	log_smgrcreate(&rel->rd_smgr->smgr_rnode.node, INIT_FORKNUM);
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 0f67a122ed..0c46372a87 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -710,6 +710,12 @@ RelationIsVisible(Oid relid)
 		elog(ERROR, "cache lookup failed for relation %u", relid);
 	relform = (Form_pg_class) GETSTRUCT(reltup);
 
+	if (relform->relkind == RELKIND_EXTERNAL)
+	{
+		ReleaseSysCache(reltup);
+		return false;
+	}
+
 	recomputeNamespacePath();
 
 	/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 3cbee108c3..8ed96cfe53 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3524,6 +3524,10 @@ getRelationDescription(StringInfo buffer, Oid relid)
 			appendStringInfo(buffer, _("foreign table %s"),
 							 relname);
 			break;
+		case RELKIND_EXTERNAL:
+			appendStringInfo(buffer, _("external table %s"),
+							 relname);
+			break;
 		default:
 			/* shouldn't get here */
 			appendStringInfo(buffer, _("relation %s"),
@@ -3992,6 +3996,9 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
 		case RELKIND_FOREIGN_TABLE:
 			appendStringInfoString(buffer, "foreign table");
 			break;
+		case RELKIND_EXTERNAL:
+			appendStringInfoString(buffer, "external table");
+			break;
 		default:
 			/* shouldn't get here */
 			appendStringInfoString(buffer, "relation");
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index a4dd306445..9568a4e11c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1222,6 +1222,12 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
 					break;
 			}
 			break;
+		case RELKIND_EXTERNAL:
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("cannot change external table \"%s\"",
+							RelationGetRelationName(resultRel))));
+			break;
 		default:
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index bf5df26009..f11388f492 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1218,6 +1218,12 @@ addRangeTableEntry(ParseState *pstate,
 	rte->relid = RelationGetRelid(rel);
 	rte->relkind = rel->rd_rel->relkind;
 
+	if (rte->relkind == RELKIND_EXTERNAL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("Cannot execute SQL queries on external tables"),
+				 parser_errposition(pstate, exprLocation((Node *)relation))));
+
 	/*
 	 * Build the list of effective column names using user-supplied aliases
 	 * and/or actual column names.
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 07e5e78caa..a5e9a71976 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -881,6 +881,7 @@ pg_relation_filenode(PG_FUNCTION_ARGS)
 		case RELKIND_INDEX:
 		case RELKIND_SEQUENCE:
 		case RELKIND_TOASTVALUE:
+		case RELKIND_EXTERNAL:
 			/* okay, these have storage */
 			if (relform->relfilenode)
 				result = relform->relfilenode;
@@ -958,6 +959,7 @@ pg_relation_filepath(PG_FUNCTION_ARGS)
 		case RELKIND_INDEX:
 		case RELKIND_SEQUENCE:
 		case RELKIND_TOASTVALUE:
+		case RELKIND_EXTERNAL:
 			/* okay, these have storage */
 
 			/* This logic should match RelationInitPhysicalAddr */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fbfa3bd123..46ac80db0d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1216,6 +1216,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 		case RELKIND_MATVIEW:
 		case RELKIND_PARTITIONED_TABLE:
 		case RELKIND_FOREIGN_TABLE: /* hari FIXME :To support COPY on foreign tables */
+		case RELKIND_EXTERNAL:
 			RelationInitTableAccessMethod(relation);
 			break;
 		default:
@@ -3303,7 +3304,8 @@ RelationBuildLocalRelation(const char *relname,
 		relkind == RELKIND_VIEW ||	/* Not exactly the storage, but underlying
 									 * tuple access, it is required */
 		relkind == RELKIND_PARTITIONED_TABLE ||
-		relkind == RELKIND_TOASTVALUE)
+		relkind == RELKIND_TOASTVALUE ||
+		relkind == RELKIND_EXTERNAL)
 		RelationInitTableAccessMethod(rel);
 
 	/*
@@ -3832,7 +3834,8 @@ RelationCacheInitializePhase3(void)
 			 relation->rd_rel->relkind == RELKIND_MATVIEW ||
 			 relation->rd_rel->relkind == RELKIND_VIEW ||
 			 relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
-			 relation->rd_rel->relkind == RELKIND_TOASTVALUE))
+			 relation->rd_rel->relkind == RELKIND_TOASTVALUE ||
+			 relation->rd_rel->relkind == RELKIND_EXTERNAL))
 		{
 			RelationInitTableAccessMethod(relation);
 			Assert(relation->rd_tableamroutine != NULL);
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index ef62c30cf9..bef4864b60 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -103,6 +103,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define		  RELKIND_FOREIGN_TABLE   'f'	/* foreign table */
 #define		  RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */
 #define		  RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */
+#define		  RELKIND_EXTERNAL 			'E'	/* External table */
 
 #define		  RELPERSISTENCE_PERMANENT	'p' /* regular table */
 #define		  RELPERSISTENCE_UNLOGGED	'u' /* unlogged permanent table */
-- 
2.16.1.windows.4

