summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2014-07-10 15:01:31 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2014-07-10 15:01:43 -0400
commit59efda3e50ca4de6a9d5aa4491464e22b6329b1e (patch)
tree23a2fe16ebc11ad9f95ef3f70c563084d6ca4007 /src
parent6a605cd6bd9f689b35676623add0de9b90978bf1 (diff)
Implement IMPORT FOREIGN SCHEMA.
This command provides an automated way to create foreign table definitions that match remote tables, thereby reducing tedium and chances for error. In this patch, we provide the necessary core-server infrastructure and implement the feature fully in the postgres_fdw foreign-data wrapper. Other wrappers will throw a "feature not supported" error until/unless they are updated. Ronan Dunklau and Michael Paquier, additional work by me
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/event_trigger.c3
-rw-r--r--src/backend/commands/foreigncmds.c144
-rw-r--r--src/backend/foreign/foreign.c41
-rw-r--r--src/backend/nodes/copyfuncs.c18
-rw-r--r--src/backend/nodes/equalfuncs.c16
-rw-r--r--src/backend/nodes/outfuncs.c16
-rw-r--r--src/backend/parser/gram.y64
-rw-r--r--src/backend/tcop/utility.c10
-rw-r--r--src/bin/psql/tab-complete.c12
-rw-r--r--src/include/commands/defrem.h1
-rw-r--r--src/include/foreign/fdwapi.h8
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/parsenodes.h25
-rw-r--r--src/include/parser/kwlist.h1
-rw-r--r--src/test/regress/expected/foreign_data.out10
-rw-r--r--src/test/regress/sql/foreign_data.sql7
16 files changed, 369 insertions, 8 deletions
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 110fe004a46..754264eb3ee 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 8ab9c439db2..ab4ed6c78ec 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -15,8 +15,8 @@
#include "access/heapam.h"
#include "access/htup_details.h"
-#include "access/xact.h"
#include "access/reloptions.h"
+#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
@@ -27,9 +27,11 @@
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/defrem.h"
+#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
+#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -37,6 +39,16 @@
#include "utils/syscache.h"
+typedef struct
+{
+ char *tablename;
+ char *cmd;
+} import_error_callback_arg;
+
+/* Internal functions */
+static void import_error_callback(void *arg);
+
+
/*
* Convert a DefElem list to the text array format that is used in
* pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
@@ -1427,3 +1439,133 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
heap_close(ftrel, RowExclusiveLock);
}
+
+/*
+ * Import a foreign schema
+ */
+void
+ImportForeignSchema(ImportForeignSchemaStmt *stmt)
+{
+ ForeignServer *server;
+ ForeignDataWrapper *fdw;
+ FdwRoutine *fdw_routine;
+ AclResult aclresult;
+ List *cmd_list;
+ ListCell *lc;
+
+ /* Check that the foreign server exists and that we have USAGE on it */
+ server = GetForeignServerByName(stmt->server_name, false);
+ aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername);
+
+ /* Check that the schema exists and we have CREATE permissions on it */
+ (void) LookupCreationNamespace(stmt->local_schema);
+
+ /* Get the FDW and check it supports IMPORT */
+ 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("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
+ fdw->fdwname)));
+
+ /* Call FDW to get a list of commands */
+ cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
+
+ /* Parse and execute each command */
+ foreach(lc, cmd_list)
+ {
+ char *cmd = (char *) lfirst(lc);
+ import_error_callback_arg callback_arg;
+ ErrorContextCallback sqlerrcontext;
+ List *raw_parsetree_list;
+ ListCell *lc2;
+
+ /*
+ * Setup error traceback support for ereport(). This is so that any
+ * error in the generated SQL will be displayed nicely.
+ */
+ callback_arg.tablename = NULL; /* not known yet */
+ callback_arg.cmd = cmd;
+ sqlerrcontext.callback = import_error_callback;
+ sqlerrcontext.arg = (void *) &callback_arg;
+ sqlerrcontext.previous = error_context_stack;
+ error_context_stack = &sqlerrcontext;
+
+ /*
+ * Parse the SQL string into a list of raw parse trees.
+ */
+ raw_parsetree_list = pg_parse_query(cmd);
+
+ /*
+ * Process each parse tree (we allow the FDW to put more than one
+ * command per string, though this isn't really advised).
+ */
+ foreach(lc2, raw_parsetree_list)
+ {
+ CreateForeignTableStmt *cstmt = lfirst(lc2);
+
+ /*
+ * Because we only allow CreateForeignTableStmt, we can skip parse
+ * analysis, rewrite, and planning steps here.
+ */
+ if (!IsA(cstmt, CreateForeignTableStmt))
+ elog(ERROR,
+ "foreign-data wrapper \"%s\" returned incorrect statement type %d",
+ fdw->fdwname, (int) nodeTag(cstmt));
+
+ /* Ignore commands for tables excluded by filter options */
+ if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
+ continue;
+
+ /* Enable reporting of current table's name on error */
+ callback_arg.tablename = cstmt->base.relation->relname;
+
+ /* Ensure creation schema is the one given in IMPORT statement */
+ cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
+
+ /* Execute statement */
+ ProcessUtility((Node *) cstmt,
+ cmd,
+ PROCESS_UTILITY_SUBCOMMAND, NULL,
+ None_Receiver, NULL);
+
+ /* Be sure to advance the command counter between subcommands */
+ CommandCounterIncrement();
+
+ callback_arg.tablename = NULL;
+ }
+
+ error_context_stack = sqlerrcontext.previous;
+ }
+}
+
+/*
+ * error context callback to let us supply the failing SQL statement's text
+ */
+static void
+import_error_callback(void *arg)
+{
+ import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
+ int syntaxerrposition;
+
+ /* If it's a syntax error, convert to internal syntax error report */
+ syntaxerrposition = geterrposition();
+ if (syntaxerrposition > 0)
+ {
+ errposition(0);
+ internalerrposition(syntaxerrposition);
+ internalerrquery(callback_arg->cmd);
+ }
+
+ if (callback_arg->tablename)
+ errcontext("importing foreign table \"%s\"",
+ callback_arg->tablename);
+}
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 6d548b7d08b..4f5f6ae362b 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -400,6 +400,47 @@ GetFdwRoutineForRelation(Relation relation, bool makecopy)
/*
+ * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA
+ *
+ * Returns TRUE if given table name should be imported according to the
+ * statement's import filter options.
+ */
+bool
+IsImportableForeignTable(const char *tablename,
+ ImportForeignSchemaStmt *stmt)
+{
+ ListCell *lc;
+
+ switch (stmt->list_type)
+ {
+ case FDW_IMPORT_SCHEMA_ALL:
+ return true;
+
+ case FDW_IMPORT_SCHEMA_LIMIT_TO:
+ foreach(lc, stmt->table_list)
+ {
+ RangeVar *rv = (RangeVar *) lfirst(lc);
+
+ if (strcmp(tablename, rv->relname) == 0)
+ return true;
+ }
+ return false;
+
+ case FDW_IMPORT_SCHEMA_EXCEPT:
+ foreach(lc, stmt->table_list)
+ {
+ RangeVar *rv = (RangeVar *) lfirst(lc);
+
+ if (strcmp(tablename, rv->relname) == 0)
+ return false;
+ }
+ return true;
+ }
+ return false; /* shouldn't get here */
+}
+
+
+/*
* deflist_to_tuplestore - Helper function to convert DefElem list to
* tuplestore usable in SRF.
*/
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 8d3d5a7c734..30885789304 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3567,6 +3567,21 @@ _copyCreateForeignTableStmt(const CreateForeignTableStmt *from)
return newnode;
}
+static ImportForeignSchemaStmt *
+_copyImportForeignSchemaStmt(const ImportForeignSchemaStmt *from)
+{
+ ImportForeignSchemaStmt *newnode = makeNode(ImportForeignSchemaStmt);
+
+ COPY_STRING_FIELD(server_name);
+ COPY_STRING_FIELD(remote_schema);
+ COPY_STRING_FIELD(local_schema);
+ COPY_SCALAR_FIELD(list_type);
+ COPY_NODE_FIELD(table_list);
+ COPY_NODE_FIELD(options);
+
+ return newnode;
+}
+
static CreateTrigStmt *
_copyCreateTrigStmt(const CreateTrigStmt *from)
{
@@ -4477,6 +4492,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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e7b49f680cf..1b07db69d73 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1769,6 +1769,19 @@ _equalCreateForeignTableStmt(const CreateForeignTableStmt *a, const CreateForeig
}
static bool
+_equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportForeignSchemaStmt *b)
+{
+ COMPARE_STRING_FIELD(server_name);
+ COMPARE_STRING_FIELD(remote_schema);
+ COMPARE_STRING_FIELD(local_schema);
+ COMPARE_SCALAR_FIELD(list_type);
+ COMPARE_NODE_FIELD(table_list);
+ COMPARE_NODE_FIELD(options);
+
+ return true;
+}
+
+static bool
_equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
{
COMPARE_STRING_FIELD(trigname);
@@ -2943,6 +2956,9 @@ 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_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c182212e62f..9573a9bb0e6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2012,6 +2012,19 @@ _outCreateForeignTableStmt(StringInfo str, const CreateForeignTableStmt *node)
}
static void
+_outImportForeignSchemaStmt(StringInfo str, const ImportForeignSchemaStmt *node)
+{
+ WRITE_NODE_TYPE("IMPORTFOREIGNSCHEMASTMT");
+
+ WRITE_STRING_FIELD(server_name);
+ WRITE_STRING_FIELD(remote_schema);
+ WRITE_STRING_FIELD(local_schema);
+ WRITE_ENUM_FIELD(list_type, ImportForeignSchemaType);
+ WRITE_NODE_FIELD(table_list);
+ WRITE_NODE_FIELD(options);
+}
+
+static void
_outIndexStmt(StringInfo str, const IndexStmt *node)
{
WRITE_NODE_TYPE("INDEXSTMT");
@@ -3119,6 +3132,9 @@ _outNode(StringInfo str, const void *obj)
case T_CreateForeignTableStmt:
_outCreateForeignTableStmt(str, obj);
break;
+ case T_ImportForeignSchemaStmt:
+ _outImportForeignSchemaStmt(str, obj);
+ break;
case T_IndexStmt:
_outIndexStmt(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index ba7d091dc79..a113809ca63 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -111,6 +111,13 @@ typedef struct PrivTarget
List *objs;
} PrivTarget;
+/* Private struct for the result of import_qualification production */
+typedef struct ImportQual
+{
+ ImportForeignSchemaType type;
+ List *table_names;
+} ImportQual;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -212,6 +219,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ResTarget *target;
struct PrivTarget *privtarget;
AccessPriv *accesspriv;
+ struct ImportQual *importqual;
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
}
@@ -238,8 +246,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 ImportForeignSchemaStmt IndexStmt InsertStmt
+ ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
@@ -322,6 +330,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <ival> defacl_privilege_target
%type <defelt> DefACLOption
%type <list> DefACLOptionList
+%type <ival> import_qualification_type
+%type <importqual> import_qualification
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
@@ -556,7 +566,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 IMPORT_P IN_P
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
@@ -802,6 +812,7 @@ stmt :
| FetchStmt
| GrantStmt
| GrantRoleStmt
+ | ImportForeignSchemaStmt
| IndexStmt
| InsertStmt
| ListenStmt
@@ -4275,6 +4286,52 @@ AlterForeignTableStmt:
/*****************************************************************************
*
* QUERY:
+ * IMPORT FOREIGN SCHEMA remote_schema
+ * [ { LIMIT TO | EXCEPT } ( table_list ) ]
+ * FROM SERVER server_name INTO local_schema [ OPTIONS (...) ]
+ *
+ ****************************************************************************/
+
+ImportForeignSchemaStmt:
+ IMPORT_P FOREIGN SCHEMA name import_qualification
+ FROM SERVER name INTO name create_generic_options
+ {
+ ImportForeignSchemaStmt *n = makeNode(ImportForeignSchemaStmt);
+ n->server_name = $8;
+ n->remote_schema = $4;
+ n->local_schema = $10;
+ n->list_type = $5->type;
+ n->table_list = $5->table_names;
+ n->options = $11;
+ $$ = (Node *) n;
+ }
+ ;
+
+import_qualification_type:
+ LIMIT TO { $$ = FDW_IMPORT_SCHEMA_LIMIT_TO; }
+ | EXCEPT { $$ = FDW_IMPORT_SCHEMA_EXCEPT; }
+ ;
+
+import_qualification:
+ import_qualification_type '(' relation_expr_list ')'
+ {
+ ImportQual *n = (ImportQual *) palloc(sizeof(ImportQual));
+ n->type = $1;
+ n->table_names = $3;
+ $$ = n;
+ }
+ | /*EMPTY*/
+ {
+ ImportQual *n = (ImportQual *) palloc(sizeof(ImportQual));
+ n->type = FDW_IMPORT_SCHEMA_ALL;
+ n->table_names = NIL;
+ $$ = n;
+ }
+ ;
+
+/*****************************************************************************
+ *
+ * QUERY:
* CREATE USER MAPPING FOR auth_ident SERVER name [OPTIONS]
*
*****************************************************************************/
@@ -12909,6 +12966,7 @@ unreserved_keyword:
| IMMEDIATE
| IMMUTABLE
| IMPLICIT_P
+ | IMPORT_P
| INCLUDING
| INCREMENT
| INDEX
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3423898c112..07e0b987212 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;
@@ -1196,6 +1197,10 @@ ProcessUtilitySlow(Node *parsetree,
RemoveUserMapping((DropUserMappingStmt *) parsetree);
break;
+ case T_ImportForeignSchemaStmt:
+ ImportForeignSchema((ImportForeignSchemaStmt *) parsetree);
+ break;
+
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
{
CompositeTypeStmt *stmt = (CompositeTypeStmt *) 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/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index bab03572352..6c2d431842c 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -876,8 +876,9 @@ psql_completion(const char *text, int start, int end)
static const char *const sql_commands[] = {
"ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER",
"COMMENT", "COMMIT", "COPY", "CREATE", "DEALLOCATE", "DECLARE",
- "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
- "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
+ "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN",
+ "FETCH", "GRANT", "IMPORT", "INSERT", "LISTEN", "LOAD", "LOCK",
+ "MOVE", "NOTIFY", "PREPARE",
"REASSIGN", "REFRESH", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
"SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
"TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
@@ -2947,6 +2948,13 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "GROUP") == 0)
COMPLETE_WITH_CONST("BY");
+/* IMPORT FOREIGN SCHEMA */
+ else if (pg_strcasecmp(prev_wd, "IMPORT") == 0)
+ COMPLETE_WITH_CONST("FOREIGN SCHEMA");
+ else if (pg_strcasecmp(prev2_wd, "IMPORT") == 0 &&
+ pg_strcasecmp(prev_wd, "FOREIGN") == 0)
+ COMPLETE_WITH_CONST("SCHEMA");
+
/* INSERT */
/* Complete INSERT with "INTO" */
else if (pg_strcasecmp(prev_wd, "INSERT") == 0)
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 5ec9374aad0..0ebdbc1c186 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 1b735dacc6d..dc0a7fc7235 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -100,6 +100,9 @@ typedef bool (*AnalyzeForeignTable_function) (Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
+typedef List *(*ImportForeignSchema_function) (ImportForeignSchemaStmt *stmt,
+ Oid serverOid);
+
/*
* 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 +147,9 @@ typedef struct FdwRoutine
/* Support functions for ANALYZE */
AnalyzeForeignTable_function AnalyzeForeignTable;
+
+ /* Support functions for IMPORT FOREIGN SCHEMA */
+ ImportForeignSchema_function ImportForeignSchema;
} FdwRoutine;
@@ -151,5 +157,7 @@ typedef struct FdwRoutine
extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
+extern bool IsImportableForeignTable(const char *tablename,
+ ImportForeignSchemaStmt *stmt);
#endif /* FDWAPI_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7b0088fdb50..067c7685f0d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -357,6 +357,7 @@ typedef enum NodeTag
T_AlterTableSpaceMoveStmt,
T_SecLabelStmt,
T_CreateForeignTableStmt,
+ T_ImportForeignSchemaStmt,
T_CreateExtensionStmt,
T_AlterExtensionStmt,
T_AlterExtensionContentsStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ff126ebca47..8364bef79f5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1791,7 +1791,7 @@ typedef struct AlterForeignServerStmt
} AlterForeignServerStmt;
/* ----------------------
- * Create FOREIGN TABLE Statements
+ * Create FOREIGN TABLE Statement
* ----------------------
*/
@@ -1832,6 +1832,29 @@ typedef struct DropUserMappingStmt
} DropUserMappingStmt;
/* ----------------------
+ * Import Foreign Schema Statement
+ * ----------------------
+ */
+
+typedef enum ImportForeignSchemaType
+{
+ FDW_IMPORT_SCHEMA_ALL, /* all relations wanted */
+ FDW_IMPORT_SCHEMA_LIMIT_TO, /* include only listed tables in import */
+ FDW_IMPORT_SCHEMA_EXCEPT /* exclude listed tables from import */
+} ImportForeignSchemaType;
+
+typedef struct ImportForeignSchemaStmt
+{
+ NodeTag type;
+ char *server_name; /* FDW server name */
+ char *remote_schema; /* remote schema name to query */
+ char *local_schema; /* local schema to create objects in */
+ ImportForeignSchemaType list_type; /* type of table list */
+ List *table_list; /* List of RangeVar */
+ List *options; /* list of options to pass to FDW */
+} ImportForeignSchemaStmt;
+
+/* ----------------------
* Create TRIGGER Statement
* ----------------------
*/
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 04e98109635..b52e50757c8 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_P, 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 ff203b201fd..e4dedb0c3ba 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, t2) 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 0f0869ee268..de9dbc8f386 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -514,6 +514,13 @@ 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, t2) 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;