From 3c2fe32558b6e8ab480e2a137cb92ea710fa7fcd Mon Sep 17 00:00:00 2001
From: "Sami Imseih (AWS)"
 <simseih@dev-dsk-simseih-1d-3940b79e.us-east-1.amazon.com>
Date: Mon, 3 Feb 2025 16:19:31 +0000
Subject: [PATCH v1 1/2] Disallow Foreign Tables with COPY FREEZE

Foreign Tables cannot take advantage of the COPY FREEZE
optimization, so it should just be disallowed.
---
 doc/src/sgml/ref/copy.sgml         |  2 +-
 src/backend/commands/copyfrom.c    | 11 +++++++++++
 src/test/regress/expected/copy.out |  8 ++++++++
 src/test/regress/sql/copy.sql      | 11 +++++++++++
 4 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/copy.sgml b/doc/src/sgml/ref/copy.sgml
index 8394402f09..090ce32876 100644
--- a/doc/src/sgml/ref/copy.sgml
+++ b/doc/src/sgml/ref/copy.sgml
@@ -237,7 +237,7 @@ COPY { <replaceable class="parameter">table_name</replaceable> [ ( <replaceable
       or truncated in the current subtransaction, there are no cursors
       open and there are no older snapshots held by this transaction.  It is
       currently not possible to perform a <command>COPY FREEZE</command> on
-      a partitioned table.
+      a partitioned or foreign table.
       This option is only allowed in <command>COPY FROM</command>.
      </para>
      <para>
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 0cbd05f560..0783f06bf7 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -740,6 +740,17 @@ CopyFrom(CopyFromState cstate)
 					 errmsg("cannot perform COPY FREEZE on a partitioned table")));
 		}
 
+		/*
+		 * Raise an error on foreign tables as it's not possible to apply
+		 * the COPY FREEZE optimization to a remote relation.
+		 */
+		if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("cannot perform COPY FREEZE on a foreign table")));
+		}
+
 		/*
 		 * Tolerate one registration for the benefit of FirstXactSnapshot.
 		 * Scan-bearing queries generally create at least two registrations,
diff --git a/src/test/regress/expected/copy.out b/src/test/regress/expected/copy.out
index f554d42c84..9036358ee6 100644
--- a/src/test/regress/expected/copy.out
+++ b/src/test/regress/expected/copy.out
@@ -325,3 +325,11 @@ SELECT tableoid::regclass, id % 2 = 0 is_even, count(*) from parted_si GROUP BY
 (2 rows)
 
 DROP TABLE parted_si;
+-- Ensure COPY FREEZE errors for foreign tables.
+BEGIN;
+CREATE FOREIGN DATA WRAPPER dummy;
+CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE test_foreign_table (a int) SERVER dummy_server;
+copy test_foreign_table from stdin with (header match, freeze on);
+ERROR:  cannot perform COPY FREEZE on a foreign table
+ROLLBACK;
diff --git a/src/test/regress/sql/copy.sql b/src/test/regress/sql/copy.sql
index f1699b66b0..687eaf79f6 100644
--- a/src/test/regress/sql/copy.sql
+++ b/src/test/regress/sql/copy.sql
@@ -348,3 +348,14 @@ COPY parted_si(id, data) FROM :'filename';
 SELECT tableoid::regclass, id % 2 = 0 is_even, count(*) from parted_si GROUP BY 1, 2 ORDER BY 1;
 
 DROP TABLE parted_si;
+
+-- Ensure COPY FREEZE errors for foreign tables.
+BEGIN;
+CREATE FOREIGN DATA WRAPPER dummy;
+CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE test_foreign_table (a int) SERVER dummy_server;
+copy test_foreign_table from stdin with (header match, freeze on);
+a
+1
+\.
+ROLLBACK;
-- 
2.47.1

