From c4b0e871658c757124dad992578da0b60fccf962 Mon Sep 17 00:00:00 2001
From: Laurenz Albe <laurenz.albe@cybertec.at>
Date: Sat, 20 Apr 2019 13:36:56 +0200
Subject: [PATCH] COPY FROM on foreign tables requires BeginForeignInsert

Commit 3d956d956a introduced COPY FROM support for foreign tables.

If a foreign data wrapper supports data modifications, but either has
not adapted to this change yet or doesn't want to support COPY FROM
for other reasons, it probably got broken by the above commit,
because COPY will just call ExecForeignInsert anyway, which might not
work because neither PlanForeignModify nor BeginForeignModify have
been called.

To avoid breaking third-party foreign data wrappers in that way, allow
COPY FROM for foreign tables only if the foreign data wrapper implements
BeginForeignInsert.
---
 doc/src/sgml/fdwhandler.sgml | 2 ++
 src/backend/commands/copy.c  | 5 +++++
 2 files changed, 7 insertions(+)

diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 2c07a86637..8cd174a204 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -724,6 +724,8 @@ BeginForeignInsert(ModifyTableState *mtstate,
      perform any initialization needed prior to the actual insertion.
      Subsequently, <function>ExecForeignInsert</function> will be called for
      each tuple to be inserted into the foreign table.
+     All foreign data wrappers that support <command>COPY FROM</command> have
+     to provide this callback function.
     </para>
 
     <para>
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index c39218f8db..944d558cd4 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2857,6 +2857,11 @@ CopyFrom(CopyState cstate)
 		resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
 		resultRelInfo->ri_FdwRoutine->BeginForeignInsert(mtstate,
 														 resultRelInfo);
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE NOT SUPPORTED),
+				 errmsg("cannot copy to foreign table \"%s\"",
+						RelationGetRelationName(cstate->rel))));
 
 	/* Prepare to catch AFTER triggers. */
 	AfterTriggerBeginQuery();
-- 
2.20.1

