diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/commands/event_trigger.c | 3 | ||||
| -rw-r--r-- | src/backend/commands/foreigncmds.c | 144 | ||||
| -rw-r--r-- | src/backend/foreign/foreign.c | 41 | ||||
| -rw-r--r-- | src/backend/nodes/copyfuncs.c | 18 | ||||
| -rw-r--r-- | src/backend/nodes/equalfuncs.c | 16 | ||||
| -rw-r--r-- | src/backend/nodes/outfuncs.c | 16 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 64 | ||||
| -rw-r--r-- | src/backend/tcop/utility.c | 10 |
8 files changed, 307 insertions, 5 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; |
