From 6ab1e806ab0708a0b139a985be81dd090cbf992f Mon Sep 17 00:00:00 2001
From: erthalion <9erthalion6@gmail.com>
Date: Wed, 17 Apr 2019 17:48:43 +0200
Subject: [PATCH 1/2] Optional tableam

Make insert/update/delete table am optional
---
 src/backend/access/table/tableamapi.c | 12 ------------
 src/backend/commands/copy.c           |  8 ++++++++
 src/backend/parser/analyze.c          | 29 +++++++++++++++++++++++++++++
 src/backend/tcop/utility.c            |  1 +
 src/include/access/tableam.h          | 34 ++++++++++++++++++++++++++++++++++
 5 files changed, 72 insertions(+), 12 deletions(-)

diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c
index bfd713f3af..0a58123a0a 100644
--- a/src/backend/access/table/tableamapi.c
+++ b/src/backend/access/table/tableamapi.c
@@ -63,18 +63,6 @@ GetTableAmRoutine(Oid amhandler)
 	Assert(routine->tuple_fetch_row_version != NULL);
 	Assert(routine->tuple_satisfies_snapshot != NULL);
 
-	Assert(routine->tuple_insert != NULL);
-
-	/*
-	 * Could be made optional, but would require throwing error during
-	 * parse-analysis.
-	 */
-	Assert(routine->tuple_insert_speculative != NULL);
-	Assert(routine->tuple_complete_speculative != NULL);
-
-	Assert(routine->multi_insert != NULL);
-	Assert(routine->tuple_delete != NULL);
-	Assert(routine->tuple_update != NULL);
 	Assert(routine->tuple_lock != NULL);
 
 	Assert(routine->relation_set_new_filenode != NULL);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c39218f8db..36e2dbf1b8 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -41,6 +41,7 @@
 #include "miscadmin.h"
 #include "optimizer/optimizer.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_expr.h"
@@ -901,6 +902,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
 											NULL, false, false);
 		rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
 
+		if (is_from && !table_support_multi_insert(rel))
+			ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("Table access method doesn't support the operation"),
+						 parser_errposition(pstate,
+											exprLocation((Node *) stmt))));
+
 		if (stmt->whereClause)
 		{
 			/* add rte to column namespace  */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 400558b552..b320ff1b26 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,6 +25,7 @@
 #include "postgres.h"
 
 #include "access/sysattr.h"
+#include "access/tableam.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -416,6 +417,13 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 										 true,
 										 ACL_DELETE);
 
+	if (!table_support_delete(pstate->p_target_relation))
+		ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("Table access method doesn't support the operation"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) stmt))));
+
 	/* grab the namespace item made by setTargetTable */
 	nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
 
@@ -553,6 +561,20 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	qry->resultRelation = setTargetTable(pstate, stmt->relation,
 										 false, false, targetPerms);
 
+	if (!table_support_insert(pstate->p_target_relation))
+		ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("Table access method doesn't support the operation"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) stmt))));
+
+	if (!table_support_speculative(pstate->p_target_relation) && isOnConflictUpdate)
+		ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("Table access method doesn't support the operation"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) stmt))));
+
 	/* Validate stmt->cols list, or build default list if no list given */
 	icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
 	Assert(list_length(icolumns) == list_length(attrnos));
@@ -2237,6 +2259,13 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 										 true,
 										 ACL_UPDATE);
 
+	if (!table_support_update(pstate->p_target_relation))
+		ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("Table access method doesn't support the operation"),
+					 parser_errposition(pstate,
+										exprLocation((Node *) stmt))));
+
 	/* grab the namespace item made by setTargetTable */
 	nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace);
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index edf24c438c..b90f047e6e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -18,6 +18,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/tableam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "access/xlog.h"
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index 6fbfcb96c9..d11cade4a0 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -1050,6 +1050,12 @@ table_insert(Relation rel, TupleTableSlot *slot, CommandId cid,
 								  bistate);
 }
 
+static inline bool
+table_support_insert(Relation rel)
+{
+	return rel->rd_tableam == NULL || rel->rd_tableam->tuple_insert != NULL;
+}
+
 /*
  * Perform a "speculative insertion". These can be backed out afterwards
  * without aborting the whole transaction.  Other sessions can wait for the
@@ -1082,6 +1088,14 @@ table_complete_speculative(Relation rel, TupleTableSlot *slot,
 												succeeded);
 }
 
+static inline bool
+table_support_speculative(Relation rel)
+{
+	return rel->rd_tableam == NULL ||
+		   (rel->rd_tableam->tuple_insert_speculative != NULL &&
+			rel->rd_tableam->tuple_complete_speculative != NULL);
+}
+
 /*
  * Insert multiple tuples into a table.
  *
@@ -1104,6 +1118,14 @@ table_multi_insert(Relation rel, TupleTableSlot **slots, int nslots,
 								  cid, options, bistate);
 }
 
+static inline bool
+table_support_multi_insert(Relation rel)
+{
+	return rel->rd_tableam == NULL ||
+		   (rel->rd_tableam->multi_insert != NULL &&
+			rel->rd_tableam->finish_bulk_insert != NULL);
+}
+
 /*
  * Delete a tuple.
  *
@@ -1140,6 +1162,12 @@ table_delete(Relation rel, ItemPointer tid, CommandId cid,
 										 wait, tmfd, changingPart);
 }
 
+static inline bool
+table_support_delete(Relation rel)
+{
+	return rel->rd_tableam == NULL || rel->rd_tableam->tuple_delete != NULL;
+}
+
 /*
  * Update a tuple.
  *
@@ -1186,6 +1214,12 @@ table_update(Relation rel, ItemPointer otid, TupleTableSlot *slot,
 										 lockmode, update_indexes);
 }
 
+static inline TM_Result
+table_support_update(Relation rel)
+{
+	return rel->rd_tableam == NULL || rel->rd_tableam->tuple_update;
+}
+
 /*
  * Lock a tuple in the specified mode.
  *
-- 
2.16.4

