From 6031c360fec5a677f05f621afc574f2505a72a54 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Mon, 30 Jun 2014 16:02:06 +0900
Subject: [PATCH 1/2] Implement IMPORT FOREIGN SCHEMA in core

This is the part to add support for IMPORT FOREIGN SCHEMA in core.
---
 doc/src/sgml/ddl.sgml                       |   5 +-
 doc/src/sgml/fdwhandler.sgml                |  33 ++++++
 doc/src/sgml/ref/allfiles.sgml              |   1 +
 doc/src/sgml/ref/import_foreign_schema.sgml | 150 ++++++++++++++++++++++++++++
 doc/src/sgml/reference.sgml                 |   1 +
 src/backend/commands/event_trigger.c        |   3 +-
 src/backend/commands/foreigncmds.c          |  76 ++++++++++++++
 src/backend/nodes/copyfuncs.c               |  31 ++++++
 src/backend/nodes/equalfuncs.c              |  27 +++++
 src/backend/parser/gram.y                   |  55 +++++++++-
 src/backend/tcop/utility.c                  |  10 ++
 src/include/commands/defrem.h               |   1 +
 src/include/foreign/fdwapi.h                |  10 ++
 src/include/nodes/nodes.h                   |   2 +
 src/include/nodes/parsenodes.h              |  28 ++++++
 src/include/parser/kwlist.h                 |   1 +
 src/test/regress/expected/foreign_data.out  |  10 ++
 src/test/regress/sql/foreign_data.sql       |   8 ++
 18 files changed, 446 insertions(+), 6 deletions(-)
 create mode 100644 doc/src/sgml/ref/import_foreign_schema.sgml

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 8ace8bd..3b7fff4 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -3069,8 +3069,9 @@ ANALYZE measurement;
     For additional information, see
     <xref linkend="sql-createforeigndatawrapper">,
     <xref linkend="sql-createserver">,
-    <xref linkend="sql-createusermapping">, and
-    <xref linkend="sql-createforeigntable">.
+    <xref linkend="sql-createusermapping">,
+    <xref linkend="sql-createforeigntable">, and
+    <xref linkend="sql-importforeignschema">.
    </para>
  </sect1>
 
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 6b5c8b7..819ff59 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -598,6 +598,39 @@ IsForeignRelUpdatable (Relation rel);
 
    </sect2>
 
+   <sect2 id="fdw-callbacks-import">
+    <title>FDW Routines For Importing Foreign Schemas</title>
+
+    <para>
+<programlisting>
+List *
+ImportForeignSchema(ForeignServer *server,
+                    const char *remote_schema,
+                    ImportForeignSchemaType import_type,
+                    List *table_list,
+                    List *options);
+</programlisting>
+
+     Obtain a list of foreign tables that will be created on server. This
+     is called when issuing <literal>IMPORT FOREIGN SCHEMA</>.
+     <literal>server</> is the foreign server used in the command;
+     <literal>local_schema</> is the local schema on which relations imported
+     will be created; <literal>remote_schema</> is the remote schema from which
+     foreign tables are imported. <literal>import_type</> is the type of import
+     done: <literal>FDW_IMPORT_SCHEMA_ALL</> means that all the foreign tables
+     should be imported (in this case <literal>table_list</> is empty),
+     <literal>FDW_IMPORT_SCHEMA_LIMIT_TO</> to include only in import the tables
+     listed in <literal>table_list</> and <literal>FDW_IMPORT_SCHEMA_EXCEPT</>
+     to exclude from import the tables listed in <literal>table_list</>.
+    </para>
+
+    <para>
+     This function should return a list of elements defined as
+     <literal>CreateForeignTableStmt</> that can be then reused by the server
+     to create the foreign tables imported.
+    </para>
+   </sect2>
+
    <sect2 id="fdw-callbacks-explain">
     <title>FDW Routines for <command>EXPLAIN</></title>
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 1b0962c..aa95d71 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -132,6 +132,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY fetch              SYSTEM "fetch.sgml">
 <!ENTITY grant              SYSTEM "grant.sgml">
 <!ENTITY insert             SYSTEM "insert.sgml">
+<!ENTITY importForeignSchema SYSTEM "import_foreign_schema.sgml">
 <!ENTITY listen             SYSTEM "listen.sgml">
 <!ENTITY load               SYSTEM "load.sgml">
 <!ENTITY lock               SYSTEM "lock.sgml">
diff --git a/doc/src/sgml/ref/import_foreign_schema.sgml b/doc/src/sgml/ref/import_foreign_schema.sgml
new file mode 100644
index 0000000..0a4c8c3
--- /dev/null
+++ b/doc/src/sgml/ref/import_foreign_schema.sgml
@@ -0,0 +1,150 @@
+<!--
+doc/src/sgml/ref/import_foreign_schema.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-IMPORTFOREIGNSCHEMA">
+ <indexterm zone="sql-importforeignschema">
+  <primary>IMPORT FOREIGN SCHEMA</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>IMPORT FOREIGN SCHEMA</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>IMPORT FOREIGN SCHEMA</refname>
+  <refpurpose>import tables from a foreign schema</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+IMPORT FOREIGN SCHEMA <replaceable class="PARAMETER">schema_name</replaceable>
+FROM SERVER <replaceable class="PARAMETER">server_name</replaceable>
+[ { LIMIT TO | EXCEPT } ( <replaceable class="PARAMETER">table_name</replaceable> [, ...] ) ]
+INTO <replaceable class="PARAMETER">local_schema</replaceable>
+[ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="SQL-IMPORTFOREIGNSCHEMA-description">
+  <title>Description</title>
+
+  <para>
+   <command>IMPORT FOREIGN SCHEMA</command> imports tables from a remote
+   schema, using a server in a local schema of the current database. The
+   tables will be owned by the user issuing the command and are imported
+   with the correct options and columns definitions.
+  </para>
+
+  <para>
+   The list of tables to import can optionnaly be limited to an explicitly
+   defined subset or can exclude a given list of tables.
+  </para>
+
+  <para>
+   To be able to import foreign tables in a local schema, user must have
+   <literal>USAGE</literal> privilege on the foreign server, as well as
+   <literal>CREATE</literal> usage on the local schema.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+
+   <varlistentry>
+    <term><replaceable class="PARAMETER">schema_name</replaceable></term>
+    <listitem>
+     <para>
+      The remote schema to import. The specific meaning of a remote schema
+      depends on the Foreign Data Wrapper implementation.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>FROM SERVER</> <replaceable class="PARAMETER">server_name</replaceable></term>
+    <listitem>
+     <para>
+      The foreign <literal>server</literal> to import schema from. The server
+      should be already defined when invocating this query.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>INTO</> <replaceable class="PARAMETER">local_schema</replaceable></term>
+    <listitem>
+     <para>
+      The <literal>schema</literal> where the imported foreign tables will
+      be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>LIMIT TO</> <replaceable class="PARAMETER">table_name</replaceable> [, ...]</term>
+    <listitem>
+     <para>
+      Table list to which the schema import is limited. All the other relations
+      on the foreign schema will be ignored.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>EXCEPT</> <replaceable class="PARAMETER">table_name</replaceable> [, ...]</term>
+    <listitem>
+     <para>
+      List of foreign tables that are excluded from the import. All the
+      relations present in the foreign schema will be imported except the
+      ones listed here.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ...] )</literal></term>
+    <listitem>
+     <para>
+      Options to be used during the foreign schema import. This allows more
+      flexible schema import for the foreign data wrapper performing the
+      import.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1 id="SQL-IMPORTFOREIGNSCHEMA-examples">
+  <title>Examples</title>
+
+  <para>
+   Create foreign schema <structname>foreign_films</> into local schema
+   <structname>films</>, which will be accessed through the server
+   <structname>film_server</>:
+
+<programlisting>
+IMPORT FOREIGN SCHEMA foreign_films
+    FROM server film_server INTO films;
+</programlisting>
+   </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createforeigntable"></member>
+   <member><xref linkend="sql-dropforeigntable"></member>
+   <member><xref linkend="sql-createserver"></member>
+   <member><xref linkend="sql-dropserver"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index a6575f5..dbe1ecb 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -160,6 +160,7 @@
    &fetch;
    &grant;
    &insert;
+   &importForeignSchema;
    &listen;
    &load;
    &lock;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 110fe00..754264e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -250,7 +250,8 @@ check_ddl_tag(const char *tag)
 		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
 		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
 		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
-		pg_strcasecmp(tag, "DROP OWNED") == 0)
+		pg_strcasecmp(tag, "DROP OWNED") == 0 ||
+		pg_strcasecmp(tag, "IMPORT FOREIGN SCHEMA") == 0)
 		return EVENT_TRIGGER_COMMAND_TAG_OK;
 
 	/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 8ab9c43..476f660 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -27,7 +27,9 @@
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
+#include "commands/tablecmds.h"
 #include "foreign/foreign.h"
+#include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
 #include "utils/acl.h"
@@ -1427,3 +1429,77 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 
 	heap_close(ftrel, RowExclusiveLock);
 }
+
+/*
+ * Import a foreign schema
+ */
+void
+ImportForeignSchema(ImportForeignSchemaStmt *stmt)
+{
+	Oid					ownerId;
+	ForeignDataWrapper *fdw;
+	ForeignServer	   *server;
+	FdwRoutine		   *fdw_routine;
+	AclResult			aclresult;
+	List			   *table_list = NULL;
+	ListCell		   *lc;
+	char			   *local_schema = strdup(stmt->local_schema);
+	char			   *remote_schema = strdup(stmt->remote_schema);
+
+	ownerId = GetUserId();
+	server = GetForeignServerByName(stmt->server_name, false);
+	aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+	/* Check the permissions on the schema */
+	LookupCreationNamespace(local_schema);
+
+	/* Sanity checks and fetch of necessary elements for next processing */
+	fdw = GetForeignDataWrapper(server->fdwid);
+	if (!OidIsValid(fdw->fdwhandler))
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("foreign-data wrapper \"%s\" has no handler",
+						fdw->fdwname)));
+	fdw_routine = GetFdwRoutine(fdw->fdwhandler);
+	if (fdw_routine->ImportForeignSchema == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_FDW_NO_SCHEMAS),
+				 errmsg("This FDW does not support schema importation")));
+
+	/* Execute FDW code path */
+	table_list = fdw_routine->ImportForeignSchema(server,
+									  stmt->remote_schema,
+									  stmt->import_list->type,
+									  stmt->import_list->table_names,
+									  stmt->options);
+
+	/* Then create objects imported */
+	foreach(lc, table_list)
+	{
+		CreateForeignTableStmt *create_stmt = lfirst(lc);
+		Oid						relOid;
+
+		/* Inform user of any incorrect object imported */
+		if (!IsA(create_stmt, CreateForeignTableStmt))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("Incorrect object has been imported for schema")));
+
+		/*
+		 * Enforce schema to the one given by caller and not the FDW
+		 * routine.
+		 */
+		create_stmt->base.relation->schemaname = strdup(local_schema);
+
+		/* Now define relation */
+		relOid = DefineRelation((CreateStmt *) create_stmt,
+								RELKIND_FOREIGN_TABLE,
+								InvalidOid);
+		Assert(relOid != InvalidOid);
+
+		/* Create the foreign table imported */
+		CreateForeignTable(create_stmt, relOid);
+	}
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 8d3d5a7..5297bea 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3567,6 +3567,31 @@ _copyCreateForeignTableStmt(const CreateForeignTableStmt *from)
 	return newnode;
 }
 
+static ImportForeignSchemaStmt *
+_copyImportForeignSchemaStmt(const ImportForeignSchemaStmt *from)
+{
+	ImportForeignSchemaStmt *newnode = makeNode(ImportForeignSchemaStmt);
+
+	COPY_STRING_FIELD(server_name);
+	COPY_STRING_FIELD(local_schema);
+	COPY_STRING_FIELD(remote_schema);
+	COPY_NODE_FIELD(import_list);
+	COPY_NODE_FIELD(options);
+
+	return newnode;
+}
+
+static ImportForeignSchemaList *
+_copyImportForeignSchemaList(const ImportForeignSchemaList *from)
+{
+	ImportForeignSchemaList *newnode = makeNode(ImportForeignSchemaList);
+
+	COPY_SCALAR_FIELD(type);
+	COPY_NODE_FIELD(table_names);
+
+	return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(const CreateTrigStmt *from)
 {
@@ -4477,6 +4502,9 @@ copyObject(const void *from)
 		case T_CreateForeignTableStmt:
 			retval = _copyCreateForeignTableStmt(from);
 			break;
+		case T_ImportForeignSchemaStmt:
+			retval = _copyImportForeignSchemaStmt(from);
+			break;
 		case T_CreateTrigStmt:
 			retval = _copyCreateTrigStmt(from);
 			break;
@@ -4637,6 +4665,9 @@ copyObject(const void *from)
 		case T_CommonTableExpr:
 			retval = _copyCommonTableExpr(from);
 			break;
+		case T_ImportForeignSchemaList:
+			retval = _copyImportForeignSchemaList(from);
+			break;
 		case T_PrivGrantee:
 			retval = _copyPrivGrantee(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e7b49f6..762187e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1769,6 +1769,27 @@ _equalCreateForeignTableStmt(const CreateForeignTableStmt *a, const CreateForeig
 }
 
 static bool
+_equalImportForeignSchemaList(const ImportForeignSchemaList *a, const ImportForeignSchemaList *b)
+{
+	COMPARE_SCALAR_FIELD(type);
+	COMPARE_NODE_FIELD(table_names);
+
+	return true;
+}
+
+static bool
+_equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportForeignSchemaStmt *b)
+{
+	COMPARE_STRING_FIELD(server_name);
+	COMPARE_STRING_FIELD(local_schema);
+	COMPARE_STRING_FIELD(remote_schema);
+	COMPARE_NODE_FIELD(import_list);
+	COMPARE_NODE_FIELD(options);
+
+	return true;
+}
+
+static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
 	COMPARE_STRING_FIELD(trigname);
@@ -2943,6 +2964,12 @@ equal(const void *a, const void *b)
 		case T_CreateForeignTableStmt:
 			retval = _equalCreateForeignTableStmt(a, b);
 			break;
+		case T_ImportForeignSchemaStmt:
+			retval = _equalImportForeignSchemaStmt(a, b);
+			break;
+		case T_ImportForeignSchemaList:
+			retval = _equalImportForeignSchemaList(a, b);
+			break;
 		case T_CreateTrigStmt:
 			retval = _equalCreateTrigStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 605c9b4..8e1772f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -238,8 +238,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
 		DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt
 		DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt
-		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
-		LockStmt NotifyStmt ExplainableStmt PreparableStmt
+		GrantStmt GrantRoleStmt IndexStmt ImportForeignSchemaStmt InsertStmt
+		ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
 		CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
 		RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
 		RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -322,6 +322,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <ival>	defacl_privilege_target
 %type <defelt>	DefACLOption
 %type <list>	DefACLOptionList
+%type <node>	OptImportForeignSchemaList
+%type <ival>	OptImportForeignSchemaType
 
 %type <list>	stmtblock stmtmulti
 				OptTableElementList TableElementList OptInherit definition
@@ -555,7 +557,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
-	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
+	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P IMPORT
 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
@@ -801,6 +803,7 @@ stmt :
 			| FetchStmt
 			| GrantStmt
 			| GrantRoleStmt
+			| ImportForeignSchemaStmt
 			| IndexStmt
 			| InsertStmt
 			| ListenStmt
@@ -4271,6 +4274,51 @@ AlterForeignTableStmt:
 				}
 		;
 
+
+/*****************************************************************************
+ *
+ *		QUERY :
+ *				IMPORT FOREIGN SCHEMA remote_schema
+ *				[ { LIMIT TO | EXCEPT } table_list ] FROM SERVER server_name
+ *				INTO local_schema [OPTIONS]
+ *
+ ****************************************************************************/
+
+ImportForeignSchemaStmt:
+			IMPORT FOREIGN SCHEMA name OptImportForeignSchemaList
+			FROM SERVER name INTO name create_generic_options
+			{
+				ImportForeignSchemaStmt *n = makeNode(ImportForeignSchemaStmt);
+				n->remote_schema = $4;
+				n->import_list = (ImportForeignSchemaList * ) $5;
+				n->server_name = $8;
+				n->local_schema = $10;
+				n->options = $11;
+				$$ = (Node *) n;
+			};
+
+OptImportForeignSchemaType:
+			LIMIT TO 				{ $$ = FDW_IMPORT_SCHEMA_LIMIT_TO; }
+			| EXCEPT 				{ $$ = FDW_IMPORT_SCHEMA_EXCEPT; }
+			;
+
+OptImportForeignSchemaList:
+			OptImportForeignSchemaType '(' relation_expr_list ')'
+			{
+				ImportForeignSchemaList *import_list = makeNode(ImportForeignSchemaList);
+				import_list->type = $1;
+				import_list->table_names = $3;
+				$$ = (Node *) import_list;
+			}
+			| /*EMPTY*/
+			{
+				ImportForeignSchemaList *import_list = makeNode(ImportForeignSchemaList);
+				import_list->type = FDW_IMPORT_SCHEMA_ALL;
+				import_list->table_names = NIL;
+				$$ = (Node *) import_list;
+			};
+
+
 /*****************************************************************************
  *
  *		QUERY:
@@ -12940,6 +12988,7 @@ unreserved_keyword:
 			| IMMEDIATE
 			| IMMUTABLE
 			| IMPLICIT_P
+			| IMPORT
 			| INCLUDING
 			| INCREMENT
 			| INDEX
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3423898..41c5889 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -202,6 +202,7 @@ check_xact_readonly(Node *parsetree)
 		case T_AlterTableSpaceOptionsStmt:
 		case T_AlterTableSpaceMoveStmt:
 		case T_CreateForeignTableStmt:
+		case T_ImportForeignSchemaStmt:
 		case T_SecLabelStmt:
 			PreventCommandIfReadOnly(CreateCommandTag(parsetree));
 			break;
@@ -1316,6 +1317,10 @@ ProcessUtilitySlow(Node *parsetree,
 				ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
 				break;
 
+			case T_ImportForeignSchemaStmt:
+				ImportForeignSchema((ImportForeignSchemaStmt *) parsetree);
+				break;
+
 			default:
 				elog(ERROR, "unrecognized node type: %d",
 					 (int) nodeTag(parsetree));
@@ -1853,6 +1858,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "CREATE FOREIGN TABLE";
 			break;
 
+		case T_ImportForeignSchemaStmt:
+			tag = "IMPORT FOREIGN SCHEMA";
+			break;
+
 		case T_DropStmt:
 			switch (((DropStmt *) parsetree)->removeType)
 			{
@@ -2518,6 +2527,7 @@ GetCommandLogLevel(Node *parsetree)
 		case T_CreateUserMappingStmt:
 		case T_AlterUserMappingStmt:
 		case T_DropUserMappingStmt:
+		case T_ImportForeignSchemaStmt:
 			lev = LOGSTMT_DDL;
 			break;
 
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 5ec9374..0ebdbc1 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -124,6 +124,7 @@ extern Oid	AlterUserMapping(AlterUserMappingStmt *stmt);
 extern Oid	RemoveUserMapping(DropUserMappingStmt *stmt);
 extern void RemoveUserMappingById(Oid umId);
 extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
+extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
 extern Datum transformGenericOptions(Oid catalogId,
 						Datum oldOptions,
 						List *options,
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 1b735da..0dab3bc 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -14,6 +14,7 @@
 
 #include "nodes/execnodes.h"
 #include "nodes/relation.h"
+#include "foreign/foreign.h"
 
 /* To avoid including explain.h here, reference ExplainState thus: */
 struct ExplainState;
@@ -100,6 +101,12 @@ typedef bool (*AnalyzeForeignTable_function) (Relation relation,
 												 AcquireSampleRowsFunc *func,
 													BlockNumber *totalpages);
 
+typedef List *(*ImportForeignSchema_function) (ForeignServer *server,
+											   const char *remote_schema,
+										 ImportForeignSchemaType import_type,
+											   List *table_list,
+											   List *options);
+
 /*
  * FdwRoutine is the struct returned by a foreign-data wrapper's handler
  * function.  It provides pointers to the callback functions needed by the
@@ -144,6 +151,9 @@ typedef struct FdwRoutine
 
 	/* Support functions for ANALYZE */
 	AnalyzeForeignTable_function AnalyzeForeignTable;
+
+	/* Support functions for IMPORT FOREIGN SCHEMA */
+	ImportForeignSchema_function ImportForeignSchema;
 } FdwRoutine;
 
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7b0088f..979f109 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -365,6 +365,7 @@ typedef enum NodeTag
 	T_RefreshMatViewStmt,
 	T_ReplicaIdentityStmt,
 	T_AlterSystemStmt,
+	T_ImportForeignSchemaStmt,
 
 	/*
 	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -407,6 +408,7 @@ typedef enum NodeTag
 	T_XmlSerialize,
 	T_WithClause,
 	T_CommonTableExpr,
+	T_ImportForeignSchemaList,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ff126eb..8ed96ab 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1263,6 +1263,34 @@ typedef enum DropBehavior
 } DropBehavior;
 
 /* ----------------------
+ * Import Foreign Schema statement
+ * ----------------------
+ */
+
+typedef enum ImportForeignSchemaType
+{
+	FDW_IMPORT_SCHEMA_ALL,		/* all relations wanted */
+	FDW_IMPORT_SCHEMA_LIMIT_TO,	/* include only table list in import */
+	FDW_IMPORT_SCHEMA_EXCEPT	/* exclude table listed from import */
+} ImportForeignSchemaType;
+
+typedef struct ImportForeignSchemaList
+{
+	ImportForeignSchemaType type;		 /* type of import */
+	List				   *table_names; /* table list controlling import */
+} ImportForeignSchemaList;
+
+typedef struct ImportForeignSchemaStmt
+{
+	NodeTag		type;
+	char	   *server_name;			/* FDW server name */
+	char	   *local_schema;		/* local schema to apply changes to */
+	char	   *remote_schema;		/* remote schema name to query */
+	ImportForeignSchemaList *import_list;	/* import type */
+	List	   *options;			/* list of options to pass to FDW */
+}	ImportForeignSchemaStmt;
+
+/* ----------------------
  *	Alter Table
  * ----------------------
  */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 61fae22..74a94e4 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -184,6 +184,7 @@ PG_KEYWORD("ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD)
 PG_KEYWORD("implicit", IMPLICIT_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("import", IMPORT, UNRESERVED_KEYWORD)
 PG_KEYWORD("in", IN_P, RESERVED_KEYWORD)
 PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD)
 PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index ff203b2..17260eb 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1193,6 +1193,16 @@ DROP TRIGGER trigtest_before_row ON foreign_schema.foreign_table_1;
 DROP TRIGGER trigtest_after_stmt ON foreign_schema.foreign_table_1;
 DROP TRIGGER trigtest_after_row ON foreign_schema.foreign_table_1;
 DROP FUNCTION dummy_trigger();
+-- IMPORT FOREIGN SCHEMA
+IMPORT FOREIGN SCHEMA s1 FROM SERVER s9 INTO public; -- ERROR
+ERROR:  foreign-data wrapper "foo" has no handler
+IMPORT FOREIGN SCHEMA s1 LIMIT TO (t1) FROM SERVER s9 INTO public; --ERROR
+ERROR:  foreign-data wrapper "foo" has no handler
+IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public; -- ERROR
+ERROR:  foreign-data wrapper "foo" has no handler
+IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public
+OPTIONS (option1 'value1', option2 'value2'); -- ERROR
+ERROR:  foreign-data wrapper "foo" has no handler
 -- DROP FOREIGN TABLE
 DROP FOREIGN TABLE no_table;                                    -- ERROR
 ERROR:  foreign table "no_table" does not exist
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 0f0869e..309a0b2 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -514,6 +514,14 @@ DROP TRIGGER trigtest_after_row ON foreign_schema.foreign_table_1;
 
 DROP FUNCTION dummy_trigger();
 
+
+-- IMPORT FOREIGN SCHEMA
+IMPORT FOREIGN SCHEMA s1 FROM SERVER s9 INTO public; -- ERROR
+IMPORT FOREIGN SCHEMA s1 LIMIT TO (t1) FROM SERVER s9 INTO public; --ERROR
+IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public; -- ERROR
+IMPORT FOREIGN SCHEMA s1 EXCEPT (t1) FROM SERVER s9 INTO public
+OPTIONS (option1 'value1', option2 'value2'); -- ERROR
+
 -- DROP FOREIGN TABLE
 DROP FOREIGN TABLE no_table;                                    -- ERROR
 DROP FOREIGN TABLE IF EXISTS no_table;
-- 
2.0.0

