From 4751c1ad97bf635bc9986d38d0763d07ba6e0e81 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 20 Mar 2020 18:42:13 -0300
Subject: [PATCH v8 2/2] Add tests

Not yet achieved consensus on this one
---
 src/bin/pg_dump/t/001_basic.pl                |  20 ++-
 src/test/modules/test_pg_dump/Makefile        |  10 +-
 .../expected/test_pg_dump_fdw.out             |  19 +++
 .../test_pg_dump/sql/test_pg_dump_fdw.sql     |   7 +
 src/test/modules/test_pg_dump/t/001_base.pl   |  57 +++++++
 .../test_pg_dump/test_pg_dump_fdw--1.0.sql    |   9 +
 .../modules/test_pg_dump/test_pg_dump_fdw.c   | 155 ++++++++++++++++++
 .../test_pg_dump/test_pg_dump_fdw.control     |   5 +
 8 files changed, 276 insertions(+), 6 deletions(-)
 create mode 100644 src/test/modules/test_pg_dump/expected/test_pg_dump_fdw.out
 create mode 100644 src/test/modules/test_pg_dump/sql/test_pg_dump_fdw.sql
 create mode 100644 src/test/modules/test_pg_dump/test_pg_dump_fdw--1.0.sql
 create mode 100644 src/test/modules/test_pg_dump/test_pg_dump_fdw.c
 create mode 100644 src/test/modules/test_pg_dump/test_pg_dump_fdw.control

diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
index 9ca8a8e608..af56ad6183 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -4,7 +4,7 @@ use warnings;
 use Config;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 74;
+use Test::More tests => 80;
 
 my $tempdir       = TestLib::tempdir;
 my $tempdir_short = TestLib::tempdir_short;
@@ -49,6 +49,24 @@ command_fails_like(
 	'pg_dump: options -s/--schema-only and -a/--data-only cannot be used together'
 );
 
+command_fails_like(
+	[ 'pg_dump', '-s', '--include-foreign-data', 'xxx' ],
+	qr/\Qpg_dump: error: options -s\/--schema-only and --include-foreign-data cannot be used together\E/,
+	'pg_dump: options -s/--schema-only and --include-foreign-data cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dump', '-j2', '--include-foreign-data', 'xxx' ],
+	qr/\Qpg_dump: error: option --include-foreign-data is not supported with parallel backup\E/,
+	'pg_dump: option --include-foreign-data is not supported with parallel backup'
+);
+
+command_fails_like(
+	[ 'pg_dump', '--include-foreign-data', '' ],
+	qr/\Qpg_dump: error: empty string is not a valid pattern in --include-foreign-data\E/,
+	'pg_dump: empty string is not a valid pattern in --include-foreign-data'
+);
+
 command_fails_like(
 	['pg_restore'],
 	qr{\Qpg_restore: error: one of -d/--dbname and -f/--file must be specified\E},
diff --git a/src/test/modules/test_pg_dump/Makefile b/src/test/modules/test_pg_dump/Makefile
index 6123b994f6..6f95a83b57 100644
--- a/src/test/modules/test_pg_dump/Makefile
+++ b/src/test/modules/test_pg_dump/Makefile
@@ -1,12 +1,12 @@
 # src/test/modules/test_pg_dump/Makefile
 
-MODULE = test_pg_dump
-PGFILEDESC = "test_pg_dump - Test pg_dump with an extension"
+MODULES = test_pg_dump_fdw
+PGFILEDESC = "test_pg_dump - Test pg_dump with extensions"
 
-EXTENSION = test_pg_dump
-DATA = test_pg_dump--1.0.sql
+EXTENSION = test_pg_dump_fdw test_pg_dump
+DATA = test_pg_dump_fdw--1.0.sql test_pg_dump--1.0.sql
 
-REGRESS = test_pg_dump
+REGRESS = test_pg_dump test_pg_dump_fdw
 TAP_TESTS = 1
 
 ifdef USE_PGXS
diff --git a/src/test/modules/test_pg_dump/expected/test_pg_dump_fdw.out b/src/test/modules/test_pg_dump/expected/test_pg_dump_fdw.out
new file mode 100644
index 0000000000..dc1b6267ee
--- /dev/null
+++ b/src/test/modules/test_pg_dump/expected/test_pg_dump_fdw.out
@@ -0,0 +1,19 @@
+CREATE EXTENSION test_pg_dump_fdw;
+CREATE SERVER pg_dump_fdw FOREIGN DATA WRAPPER test_pg_dump_fdw;
+CREATE FOREIGN TABLE test_pg_dump_fdw_t (a INTEGER, b INTEGER) SERVER pg_dump_fdw;
+SELECT * FROM test_pg_dump_fdw_t;
+ a  | b  
+----+----
+  0 |  0
+  1 |  1
+  2 |  2
+  3 |  3
+  4 |  4
+  5 |  5
+  6 |  6
+  7 |  7
+  8 |  8
+  9 |  9
+ 10 | 10
+(11 rows)
+
diff --git a/src/test/modules/test_pg_dump/sql/test_pg_dump_fdw.sql b/src/test/modules/test_pg_dump/sql/test_pg_dump_fdw.sql
new file mode 100644
index 0000000000..06ad1d51a0
--- /dev/null
+++ b/src/test/modules/test_pg_dump/sql/test_pg_dump_fdw.sql
@@ -0,0 +1,7 @@
+CREATE EXTENSION test_pg_dump_fdw;
+
+CREATE SERVER pg_dump_fdw FOREIGN DATA WRAPPER test_pg_dump_fdw;
+
+CREATE FOREIGN TABLE test_pg_dump_fdw_t (a INTEGER, b INTEGER) SERVER pg_dump_fdw;
+
+SELECT * FROM test_pg_dump_fdw_t;
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index ae120a5ee3..41f93f5e4a 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -135,6 +135,13 @@ my %pgdump_runs = (
 			"$tempdir/defaults_tar_format.tar",
 		],
 	},
+	include_foreign_data => {
+		dump_cmd => [
+			'pg_dump',
+			'--include-foreign-data=test_pg_dump_fdw_server',
+			"--file=$tempdir/include_foreign_data.sql",
+		],
+	},
 	pg_dumpall_globals => {
 		dump_cmd => [
 			'pg_dumpall',                             '--no-sync',
@@ -220,6 +227,7 @@ my %full_runs = (
 	createdb        => 1,
 	defaults        => 1,
 	no_privs        => 1,
+	include_foreign_data => 1,
 	no_owner        => 1,);
 
 my %tests = (
@@ -569,6 +577,55 @@ my %tests = (
 			schema_only      => 1,
 			section_pre_data => 1,
 		},
+	},
+
+	'CREATE EXTENSION test_pg_dump_fdw' => {
+		create_order => 2,
+		create_sql   => 'CREATE EXTENSION test_pg_dump_fdw;',
+		regexp => qr/^
+			\QCREATE EXTENSION IF NOT EXISTS test_pg_dump_fdw WITH SCHEMA public;\E
+			\n/xm,
+		like => {
+			%full_runs,
+			include_foreign_data => 1,
+			schema_only => 1,
+			section_pre_data => 1,
+		},
+		unlike => { binary_upgrade => 1, },
+	},
+
+	'CREATE SERVER test_pg_dump_fdw_server FOREIGN DATA WRAPPER test_pg_dump_fdw' => {
+		create_order => 3,
+		create_sql   => 'CREATE SERVER test_pg_dump_fdw_server FOREIGN DATA WRAPPER test_pg_dump_fdw;',
+		regexp => qr/^
+			\QCREATE SERVER test_pg_dump_fdw_server FOREIGN DATA WRAPPER test_pg_dump_fdw;\E
+			\n/xm,
+		like => {
+			%full_runs,
+			include_foreign_data => 1,
+			schema_only => 1,
+			section_pre_data => 1,
+		},
+	},
+
+	'include foreign data' => {
+		create_order => 9,
+		create_sql => 'CREATE FOREIGN TABLE t (a INTEGER, b INTEGER) SERVER test_pg_dump_fdw_server;',
+		regexp => qr/^
+			\QCOPY public.t (a, b) FROM stdin;\E\n
+			\Q0	0\E\n
+			\Q1	1\E\n
+			\Q2	2\E\n
+			\Q3	3\E\n
+			\Q4	4\E\n
+			\Q5	5\E\n
+			\Q6	6\E\n
+			\Q7	7\E\n
+			\Q8	8\E\n
+			\Q9	9\E\n
+			\Q10	10\E\n
+			/xm,
+		like => { include_foreign_data => 1, },
 	},);
 
 #########################################
diff --git a/src/test/modules/test_pg_dump/test_pg_dump_fdw--1.0.sql b/src/test/modules/test_pg_dump/test_pg_dump_fdw--1.0.sql
new file mode 100644
index 0000000000..88931393d0
--- /dev/null
+++ b/src/test/modules/test_pg_dump/test_pg_dump_fdw--1.0.sql
@@ -0,0 +1,9 @@
+\echo Use "CREATE EXTENSION test_pg_dump_fdw" to load this file. \quit
+
+CREATE FUNCTION test_pg_dump_fdw_handler()
+RETURNS fdw_handler
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+CREATE FOREIGN DATA WRAPPER test_pg_dump_fdw
+HANDLER test_pg_dump_fdw_handler;
diff --git a/src/test/modules/test_pg_dump/test_pg_dump_fdw.c b/src/test/modules/test_pg_dump/test_pg_dump_fdw.c
new file mode 100644
index 0000000000..1503bb1a81
--- /dev/null
+++ b/src/test/modules/test_pg_dump/test_pg_dump_fdw.c
@@ -0,0 +1,155 @@
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "foreign/fdwapi.h"
+#include "foreign/foreign.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/planmain.h"
+#include "optimizer/restrictinfo.h"
+
+static int curr_row = 0;
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_pg_dump_fdw_handler);
+
+static void dumptestGetForeignRelSize(PlannerInfo *root,
+									  RelOptInfo *baserel,
+									  Oid foreigntableid);
+static void dumptestGetForeignPaths(PlannerInfo *root,
+									RelOptInfo *baserel,
+									Oid foreigntableid);
+static ForeignScan * dumptestGetForeignPlan(PlannerInfo *root,
+								   RelOptInfo *baserel,
+								   Oid foreigntableid,
+								   ForeignPath *best_path,
+								   List *tlist,
+								   List *scan_clauses,
+								   Plan *outer_plan);
+static void dumptestBeginForeignScan(ForeignScanState *node,
+									 int eflags);
+static TupleTableSlot * dumptestIterateForeignScan(ForeignScanState *node);
+static void dumptestReScanForeignScan(ForeignScanState *node);
+static void dumptestEndForeignScan(ForeignScanState *node);
+
+/*
+ * Handler function
+ */
+Datum
+test_pg_dump_fdw_handler(PG_FUNCTION_ARGS)
+{
+	FdwRoutine *fdwroutine = makeNode(FdwRoutine);
+
+	fdwroutine->GetForeignRelSize = dumptestGetForeignRelSize;
+	fdwroutine->GetForeignPaths = dumptestGetForeignPaths;
+	fdwroutine->GetForeignPlan = dumptestGetForeignPlan;
+	fdwroutine->BeginForeignScan = dumptestBeginForeignScan;
+	fdwroutine->IterateForeignScan = dumptestIterateForeignScan;
+	fdwroutine->ReScanForeignScan = dumptestReScanForeignScan;
+	fdwroutine->EndForeignScan = dumptestEndForeignScan;
+
+	PG_RETURN_POINTER(fdwroutine);
+}
+
+static void
+dumptestGetForeignRelSize(PlannerInfo *root,
+						  RelOptInfo *baserel,
+						  Oid foreigntableid)
+{
+	baserel->rows = 1;
+}
+
+static void
+dumptestGetForeignPaths(PlannerInfo *root,
+						RelOptInfo *baserel,
+						Oid foreigntableid)
+{
+	add_path(baserel, (Path *)
+			 create_foreignscan_path(root, baserel,
+									 NULL /* default pathtarget */,
+									 baserel->rows,
+									 1,
+									 1,
+									 NIL,
+									 baserel->lateral_relids,
+									 NULL,
+									 NIL));
+}
+
+static ForeignScan *
+dumptestGetForeignPlan(PlannerInfo *root,
+					   RelOptInfo *baserel,
+					   Oid foreigntableid,
+					   ForeignPath *best_path,
+					   List *tlist,
+					   List *scan_clauses,
+					   Plan *outer_plan)
+{
+	scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+	return make_foreignscan(tlist,
+							scan_clauses,
+							baserel->relid,
+							NIL,
+							best_path->fdw_private,
+							NIL,
+							NIL,
+							outer_plan);
+}
+
+static void
+dumptestBeginForeignScan(ForeignScanState *node, int eflags)
+{
+	TupleDesc		desc;
+
+	desc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+
+	for (int i = 0; i < desc->natts; i++)
+	{
+		if (desc->attrs[i].atttypid != INT4OID)
+			ereport(ERROR,
+					(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
+					 errmsg("test_pg_dump_fdw only supports INT4 columns")));
+	}
+}
+
+static TupleTableSlot *
+dumptestIterateForeignScan(ForeignScanState *node)
+{
+	TupleTableSlot *slot;
+	TupleDesc		desc;
+
+	/* limit the testcase to contain 10 rows */
+	if (curr_row > 10)
+		return NULL;
+
+	slot = node->ss.ss_ScanTupleSlot;
+	desc = slot->tts_tupleDescriptor;
+
+	ExecClearTuple(slot);
+
+	for (int i = 0; i < desc->natts; i++)
+	{
+		slot->tts_isnull[i] = false;
+		slot->tts_values[i] = Int32GetDatum(curr_row);
+	}
+
+	ExecStoreVirtualTuple(slot);
+	curr_row++;
+
+	return slot;
+}
+
+static void
+dumptestReScanForeignScan(ForeignScanState *node)
+{
+	(void) node;
+	curr_row = 0;
+}
+
+static void
+dumptestEndForeignScan(ForeignScanState *node)
+{
+	(void) node;
+	curr_row = 0;
+}
diff --git a/src/test/modules/test_pg_dump/test_pg_dump_fdw.control b/src/test/modules/test_pg_dump/test_pg_dump_fdw.control
new file mode 100644
index 0000000000..cc4a37c441
--- /dev/null
+++ b/src/test/modules/test_pg_dump/test_pg_dump_fdw.control
@@ -0,0 +1,5 @@
+# test_pg_dump_fdw
+comment = 'hardcoded foreign-data wrapper for testing dumping foreign data'
+default_version = '1.0'
+module_pathname = '$libdir/test_pg_dump_fdw'
+relocatable = true
-- 
2.20.1

