LCOV - code coverage report
Current view: top level - src/backend/commands - foreigncmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 533 565 94.3 %
Date: 2020-06-01 10:07:15 Functions: 24 25 96.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * foreigncmds.c
       4             :  *    foreign-data wrapper/server creation/manipulation commands
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/commands/foreigncmds.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/htup_details.h"
      17             : #include "access/reloptions.h"
      18             : #include "access/table.h"
      19             : #include "access/xact.h"
      20             : #include "catalog/catalog.h"
      21             : #include "catalog/dependency.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/objectaccess.h"
      24             : #include "catalog/pg_foreign_data_wrapper.h"
      25             : #include "catalog/pg_foreign_server.h"
      26             : #include "catalog/pg_foreign_table.h"
      27             : #include "catalog/pg_proc.h"
      28             : #include "catalog/pg_type.h"
      29             : #include "catalog/pg_user_mapping.h"
      30             : #include "commands/defrem.h"
      31             : #include "foreign/fdwapi.h"
      32             : #include "foreign/foreign.h"
      33             : #include "miscadmin.h"
      34             : #include "parser/parse_func.h"
      35             : #include "tcop/utility.h"
      36             : #include "utils/acl.h"
      37             : #include "utils/builtins.h"
      38             : #include "utils/lsyscache.h"
      39             : #include "utils/rel.h"
      40             : #include "utils/syscache.h"
      41             : 
      42             : 
      43             : typedef struct
      44             : {
      45             :     char       *tablename;
      46             :     char       *cmd;
      47             : } import_error_callback_arg;
      48             : 
      49             : /* Internal functions */
      50             : static void import_error_callback(void *arg);
      51             : 
      52             : 
      53             : /*
      54             :  * Convert a DefElem list to the text array format that is used in
      55             :  * pg_foreign_data_wrapper, pg_foreign_server, pg_user_mapping, and
      56             :  * pg_foreign_table.
      57             :  *
      58             :  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
      59             :  * if the list is empty.
      60             :  *
      61             :  * Note: The array is usually stored to database without further
      62             :  * processing, hence any validation should be done before this
      63             :  * conversion.
      64             :  */
      65             : static Datum
      66         908 : optionListToArray(List *options)
      67             : {
      68         908 :     ArrayBuildState *astate = NULL;
      69             :     ListCell   *cell;
      70             : 
      71        2118 :     foreach(cell, options)
      72             :     {
      73        1210 :         DefElem    *def = lfirst(cell);
      74             :         const char *value;
      75             :         Size        len;
      76             :         text       *t;
      77             : 
      78        1210 :         value = defGetString(def);
      79        1210 :         len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
      80        1210 :         t = palloc(len + 1);
      81        1210 :         SET_VARSIZE(t, len);
      82        1210 :         sprintf(VARDATA(t), "%s=%s", def->defname, value);
      83             : 
      84        1210 :         astate = accumArrayResult(astate, PointerGetDatum(t),
      85             :                                   false, TEXTOID,
      86             :                                   CurrentMemoryContext);
      87             :     }
      88             : 
      89         908 :     if (astate)
      90         598 :         return makeArrayResult(astate, CurrentMemoryContext);
      91             : 
      92         310 :     return PointerGetDatum(NULL);
      93             : }
      94             : 
      95             : 
      96             : /*
      97             :  * Transform a list of DefElem into text array format.  This is substantially
      98             :  * the same thing as optionListToArray(), except we recognize SET/ADD/DROP
      99             :  * actions for modifying an existing list of options, which is passed in
     100             :  * Datum form as oldOptions.  Also, if fdwvalidator isn't InvalidOid
     101             :  * it specifies a validator function to call on the result.
     102             :  *
     103             :  * Returns the array in the form of a Datum, or PointerGetDatum(NULL)
     104             :  * if the list is empty.
     105             :  *
     106             :  * This is used by CREATE/ALTER of FOREIGN DATA WRAPPER/SERVER/USER MAPPING/
     107             :  * FOREIGN TABLE.
     108             :  */
     109             : Datum
     110         924 : transformGenericOptions(Oid catalogId,
     111             :                         Datum oldOptions,
     112             :                         List *options,
     113             :                         Oid fdwvalidator)
     114             : {
     115         924 :     List       *resultOptions = untransformRelOptions(oldOptions);
     116             :     ListCell   *optcell;
     117             :     Datum       result;
     118             : 
     119        1908 :     foreach(optcell, options)
     120             :     {
     121        1000 :         DefElem    *od = lfirst(optcell);
     122             :         ListCell   *cell;
     123             : 
     124             :         /*
     125             :          * Find the element in resultOptions.  We need this for validation in
     126             :          * all cases.
     127             :          */
     128        2212 :         foreach(cell, resultOptions)
     129             :         {
     130        1334 :             DefElem    *def = lfirst(cell);
     131             : 
     132        1334 :             if (strcmp(def->defname, od->defname) == 0)
     133         122 :                 break;
     134             :         }
     135             : 
     136             :         /*
     137             :          * It is possible to perform multiple SET/DROP actions on the same
     138             :          * option.  The standard permits this, as long as the options to be
     139             :          * added are unique.  Note that an unspecified action is taken to be
     140             :          * ADD.
     141             :          */
     142        1000 :         switch (od->defaction)
     143             :         {
     144          54 :             case DEFELEM_DROP:
     145          54 :                 if (!cell)
     146           4 :                     ereport(ERROR,
     147             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
     148             :                              errmsg("option \"%s\" not found",
     149             :                                     od->defname)));
     150          50 :                 resultOptions = list_delete_cell(resultOptions, cell);
     151          50 :                 break;
     152             : 
     153          68 :             case DEFELEM_SET:
     154          68 :                 if (!cell)
     155           4 :                     ereport(ERROR,
     156             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
     157             :                              errmsg("option \"%s\" not found",
     158             :                                     od->defname)));
     159          64 :                 lfirst(cell) = od;
     160          64 :                 break;
     161             : 
     162         878 :             case DEFELEM_ADD:
     163             :             case DEFELEM_UNSPEC:
     164         878 :                 if (cell)
     165           8 :                     ereport(ERROR,
     166             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
     167             :                              errmsg("option \"%s\" provided more than once",
     168             :                                     od->defname)));
     169         870 :                 resultOptions = lappend(resultOptions, od);
     170         870 :                 break;
     171             : 
     172           0 :             default:
     173           0 :                 elog(ERROR, "unrecognized action %d on option \"%s\"",
     174             :                      (int) od->defaction, od->defname);
     175             :                 break;
     176             :         }
     177             :     }
     178             : 
     179         908 :     result = optionListToArray(resultOptions);
     180             : 
     181         908 :     if (OidIsValid(fdwvalidator))
     182             :     {
     183         492 :         Datum       valarg = result;
     184             : 
     185             :         /*
     186             :          * Pass a null options list as an empty array, so that validators
     187             :          * don't have to be declared non-strict to handle the case.
     188             :          */
     189         492 :         if (DatumGetPointer(valarg) == NULL)
     190          84 :             valarg = PointerGetDatum(construct_empty_array(TEXTOID));
     191         492 :         OidFunctionCall2(fdwvalidator, valarg, ObjectIdGetDatum(catalogId));
     192             :     }
     193             : 
     194         816 :     return result;
     195             : }
     196             : 
     197             : 
     198             : /*
     199             :  * Internal workhorse for changing a data wrapper's owner.
     200             :  *
     201             :  * Allow this only for superusers; also the new owner must be a
     202             :  * superuser.
     203             :  */
     204             : static void
     205          14 : AlterForeignDataWrapperOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
     206             : {
     207             :     Form_pg_foreign_data_wrapper form;
     208             :     Datum       repl_val[Natts_pg_foreign_data_wrapper];
     209             :     bool        repl_null[Natts_pg_foreign_data_wrapper];
     210             :     bool        repl_repl[Natts_pg_foreign_data_wrapper];
     211             :     Acl        *newAcl;
     212             :     Datum       aclDatum;
     213             :     bool        isNull;
     214             : 
     215          14 :     form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
     216             : 
     217             :     /* Must be a superuser to change a FDW owner */
     218          14 :     if (!superuser())
     219           4 :         ereport(ERROR,
     220             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     221             :                  errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
     222             :                         NameStr(form->fdwname)),
     223             :                  errhint("Must be superuser to change owner of a foreign-data wrapper.")));
     224             : 
     225             :     /* New owner must also be a superuser */
     226          10 :     if (!superuser_arg(newOwnerId))
     227           4 :         ereport(ERROR,
     228             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     229             :                  errmsg("permission denied to change owner of foreign-data wrapper \"%s\"",
     230             :                         NameStr(form->fdwname)),
     231             :                  errhint("The owner of a foreign-data wrapper must be a superuser.")));
     232             : 
     233           6 :     if (form->fdwowner != newOwnerId)
     234             :     {
     235           4 :         memset(repl_null, false, sizeof(repl_null));
     236           4 :         memset(repl_repl, false, sizeof(repl_repl));
     237             : 
     238           4 :         repl_repl[Anum_pg_foreign_data_wrapper_fdwowner - 1] = true;
     239           4 :         repl_val[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(newOwnerId);
     240             : 
     241           4 :         aclDatum = heap_getattr(tup,
     242             :                                 Anum_pg_foreign_data_wrapper_fdwacl,
     243             :                                 RelationGetDescr(rel),
     244             :                                 &isNull);
     245             :         /* Null ACLs do not require changes */
     246           4 :         if (!isNull)
     247             :         {
     248           0 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
     249             :                                  form->fdwowner, newOwnerId);
     250           0 :             repl_repl[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
     251           0 :             repl_val[Anum_pg_foreign_data_wrapper_fdwacl - 1] = PointerGetDatum(newAcl);
     252             :         }
     253             : 
     254           4 :         tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
     255             :                                 repl_repl);
     256             : 
     257           4 :         CatalogTupleUpdate(rel, &tup->t_self, tup);
     258             : 
     259             :         /* Update owner dependency reference */
     260           4 :         changeDependencyOnOwner(ForeignDataWrapperRelationId,
     261             :                                 form->oid,
     262             :                                 newOwnerId);
     263             :     }
     264             : 
     265           6 :     InvokeObjectPostAlterHook(ForeignDataWrapperRelationId,
     266             :                               form->oid, 0);
     267           6 : }
     268             : 
     269             : /*
     270             :  * Change foreign-data wrapper owner -- by name
     271             :  *
     272             :  * Note restrictions in the "_internal" function, above.
     273             :  */
     274             : ObjectAddress
     275          14 : AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
     276             : {
     277             :     Oid         fdwId;
     278             :     HeapTuple   tup;
     279             :     Relation    rel;
     280             :     ObjectAddress address;
     281             :     Form_pg_foreign_data_wrapper form;
     282             : 
     283             : 
     284          14 :     rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
     285             : 
     286          14 :     tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(name));
     287             : 
     288          14 :     if (!HeapTupleIsValid(tup))
     289           0 :         ereport(ERROR,
     290             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     291             :                  errmsg("foreign-data wrapper \"%s\" does not exist", name)));
     292             : 
     293          14 :     form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
     294          14 :     fdwId = form->oid;
     295             : 
     296          14 :     AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
     297             : 
     298           6 :     ObjectAddressSet(address, ForeignDataWrapperRelationId, fdwId);
     299             : 
     300           6 :     heap_freetuple(tup);
     301             : 
     302           6 :     table_close(rel, RowExclusiveLock);
     303             : 
     304           6 :     return address;
     305             : }
     306             : 
     307             : /*
     308             :  * Change foreign-data wrapper owner -- by OID
     309             :  *
     310             :  * Note restrictions in the "_internal" function, above.
     311             :  */
     312             : void
     313           0 : AlterForeignDataWrapperOwner_oid(Oid fwdId, Oid newOwnerId)
     314             : {
     315             :     HeapTuple   tup;
     316             :     Relation    rel;
     317             : 
     318           0 :     rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
     319             : 
     320           0 :     tup = SearchSysCacheCopy1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fwdId));
     321             : 
     322           0 :     if (!HeapTupleIsValid(tup))
     323           0 :         ereport(ERROR,
     324             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     325             :                  errmsg("foreign-data wrapper with OID %u does not exist", fwdId)));
     326             : 
     327           0 :     AlterForeignDataWrapperOwner_internal(rel, tup, newOwnerId);
     328             : 
     329           0 :     heap_freetuple(tup);
     330             : 
     331           0 :     table_close(rel, RowExclusiveLock);
     332           0 : }
     333             : 
     334             : /*
     335             :  * Internal workhorse for changing a foreign server's owner
     336             :  */
     337             : static void
     338          54 : AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
     339             : {
     340             :     Form_pg_foreign_server form;
     341             :     Datum       repl_val[Natts_pg_foreign_server];
     342             :     bool        repl_null[Natts_pg_foreign_server];
     343             :     bool        repl_repl[Natts_pg_foreign_server];
     344             :     Acl        *newAcl;
     345             :     Datum       aclDatum;
     346             :     bool        isNull;
     347             : 
     348          54 :     form = (Form_pg_foreign_server) GETSTRUCT(tup);
     349             : 
     350          54 :     if (form->srvowner != newOwnerId)
     351             :     {
     352             :         /* Superusers can always do it */
     353          48 :         if (!superuser())
     354             :         {
     355             :             Oid         srvId;
     356             :             AclResult   aclresult;
     357             : 
     358          20 :             srvId = form->oid;
     359             : 
     360             :             /* Must be owner */
     361          20 :             if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
     362           8 :                 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
     363           8 :                                NameStr(form->srvname));
     364             : 
     365             :             /* Must be able to become new owner */
     366          12 :             check_is_member_of_role(GetUserId(), newOwnerId);
     367             : 
     368             :             /* New owner must have USAGE privilege on foreign-data wrapper */
     369           8 :             aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE);
     370           8 :             if (aclresult != ACLCHECK_OK)
     371             :             {
     372           4 :                 ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw);
     373             : 
     374           4 :                 aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
     375             :             }
     376             :         }
     377             : 
     378          32 :         memset(repl_null, false, sizeof(repl_null));
     379          32 :         memset(repl_repl, false, sizeof(repl_repl));
     380             : 
     381          32 :         repl_repl[Anum_pg_foreign_server_srvowner - 1] = true;
     382          32 :         repl_val[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(newOwnerId);
     383             : 
     384          32 :         aclDatum = heap_getattr(tup,
     385             :                                 Anum_pg_foreign_server_srvacl,
     386             :                                 RelationGetDescr(rel),
     387             :                                 &isNull);
     388             :         /* Null ACLs do not require changes */
     389          32 :         if (!isNull)
     390             :         {
     391          12 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
     392             :                                  form->srvowner, newOwnerId);
     393          12 :             repl_repl[Anum_pg_foreign_server_srvacl - 1] = true;
     394          12 :             repl_val[Anum_pg_foreign_server_srvacl - 1] = PointerGetDatum(newAcl);
     395             :         }
     396             : 
     397          32 :         tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
     398             :                                 repl_repl);
     399             : 
     400          32 :         CatalogTupleUpdate(rel, &tup->t_self, tup);
     401             : 
     402             :         /* Update owner dependency reference */
     403          32 :         changeDependencyOnOwner(ForeignServerRelationId, form->oid,
     404             :                                 newOwnerId);
     405             :     }
     406             : 
     407          38 :     InvokeObjectPostAlterHook(ForeignServerRelationId,
     408             :                               form->oid, 0);
     409          38 : }
     410             : 
     411             : /*
     412             :  * Change foreign server owner -- by name
     413             :  */
     414             : ObjectAddress
     415          46 : AlterForeignServerOwner(const char *name, Oid newOwnerId)
     416             : {
     417             :     Oid         servOid;
     418             :     HeapTuple   tup;
     419             :     Relation    rel;
     420             :     ObjectAddress address;
     421             :     Form_pg_foreign_server form;
     422             : 
     423          46 :     rel = table_open(ForeignServerRelationId, RowExclusiveLock);
     424             : 
     425          46 :     tup = SearchSysCacheCopy1(FOREIGNSERVERNAME, CStringGetDatum(name));
     426             : 
     427          46 :     if (!HeapTupleIsValid(tup))
     428           0 :         ereport(ERROR,
     429             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     430             :                  errmsg("server \"%s\" does not exist", name)));
     431             : 
     432          46 :     form = (Form_pg_foreign_server) GETSTRUCT(tup);
     433          46 :     servOid = form->oid;
     434             : 
     435          46 :     AlterForeignServerOwner_internal(rel, tup, newOwnerId);
     436             : 
     437          30 :     ObjectAddressSet(address, ForeignServerRelationId, servOid);
     438             : 
     439          30 :     heap_freetuple(tup);
     440             : 
     441          30 :     table_close(rel, RowExclusiveLock);
     442             : 
     443          30 :     return address;
     444             : }
     445             : 
     446             : /*
     447             :  * Change foreign server owner -- by OID
     448             :  */
     449             : void
     450           8 : AlterForeignServerOwner_oid(Oid srvId, Oid newOwnerId)
     451             : {
     452             :     HeapTuple   tup;
     453             :     Relation    rel;
     454             : 
     455           8 :     rel = table_open(ForeignServerRelationId, RowExclusiveLock);
     456             : 
     457           8 :     tup = SearchSysCacheCopy1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
     458             : 
     459           8 :     if (!HeapTupleIsValid(tup))
     460           0 :         ereport(ERROR,
     461             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     462             :                  errmsg("foreign server with OID %u does not exist", srvId)));
     463             : 
     464           8 :     AlterForeignServerOwner_internal(rel, tup, newOwnerId);
     465             : 
     466           8 :     heap_freetuple(tup);
     467             : 
     468           8 :     table_close(rel, RowExclusiveLock);
     469           8 : }
     470             : 
     471             : /*
     472             :  * Convert a handler function name passed from the parser to an Oid.
     473             :  */
     474             : static Oid
     475          28 : lookup_fdw_handler_func(DefElem *handler)
     476             : {
     477             :     Oid         handlerOid;
     478             : 
     479          28 :     if (handler == NULL || handler->arg == NULL)
     480           0 :         return InvalidOid;
     481             : 
     482             :     /* handlers have no arguments */
     483          28 :     handlerOid = LookupFuncName((List *) handler->arg, 0, NULL, false);
     484             : 
     485             :     /* check that handler has correct return type */
     486          28 :     if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
     487           8 :         ereport(ERROR,
     488             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     489             :                  errmsg("function %s must return type %s",
     490             :                         NameListToString((List *) handler->arg), "fdw_handler")));
     491             : 
     492          20 :     return handlerOid;
     493             : }
     494             : 
     495             : /*
     496             :  * Convert a validator function name passed from the parser to an Oid.
     497             :  */
     498             : static Oid
     499          34 : lookup_fdw_validator_func(DefElem *validator)
     500             : {
     501             :     Oid         funcargtypes[2];
     502             : 
     503          34 :     if (validator == NULL || validator->arg == NULL)
     504           4 :         return InvalidOid;
     505             : 
     506             :     /* validators take text[], oid */
     507          30 :     funcargtypes[0] = TEXTARRAYOID;
     508          30 :     funcargtypes[1] = OIDOID;
     509             : 
     510          30 :     return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
     511             :     /* validator's return value is ignored, so we don't check the type */
     512             : }
     513             : 
     514             : /*
     515             :  * Process function options of CREATE/ALTER FDW
     516             :  */
     517             : static void
     518         156 : parse_func_options(List *func_options,
     519             :                    bool *handler_given, Oid *fdwhandler,
     520             :                    bool *validator_given, Oid *fdwvalidator)
     521             : {
     522             :     ListCell   *cell;
     523             : 
     524         156 :     *handler_given = false;
     525         156 :     *validator_given = false;
     526             :     /* return InvalidOid if not given */
     527         156 :     *fdwhandler = InvalidOid;
     528         156 :     *fdwvalidator = InvalidOid;
     529             : 
     530         202 :     foreach(cell, func_options)
     531             :     {
     532          70 :         DefElem    *def = (DefElem *) lfirst(cell);
     533             : 
     534          70 :         if (strcmp(def->defname, "handler") == 0)
     535             :         {
     536          36 :             if (*handler_given)
     537           8 :                 ereport(ERROR,
     538             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     539             :                          errmsg("conflicting or redundant options")));
     540          28 :             *handler_given = true;
     541          28 :             *fdwhandler = lookup_fdw_handler_func(def);
     542             :         }
     543          34 :         else if (strcmp(def->defname, "validator") == 0)
     544             :         {
     545          34 :             if (*validator_given)
     546           0 :                 ereport(ERROR,
     547             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     548             :                          errmsg("conflicting or redundant options")));
     549          34 :             *validator_given = true;
     550          34 :             *fdwvalidator = lookup_fdw_validator_func(def);
     551             :         }
     552             :         else
     553           0 :             elog(ERROR, "option \"%s\" not recognized",
     554             :                  def->defname);
     555             :     }
     556         132 : }
     557             : 
     558             : /*
     559             :  * Create a foreign-data wrapper
     560             :  */
     561             : ObjectAddress
     562         114 : CreateForeignDataWrapper(CreateFdwStmt *stmt)
     563             : {
     564             :     Relation    rel;
     565             :     Datum       values[Natts_pg_foreign_data_wrapper];
     566             :     bool        nulls[Natts_pg_foreign_data_wrapper];
     567             :     HeapTuple   tuple;
     568             :     Oid         fdwId;
     569             :     bool        handler_given;
     570             :     bool        validator_given;
     571             :     Oid         fdwhandler;
     572             :     Oid         fdwvalidator;
     573             :     Datum       fdwoptions;
     574             :     Oid         ownerId;
     575             :     ObjectAddress myself;
     576             :     ObjectAddress referenced;
     577             : 
     578         114 :     rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
     579             : 
     580             :     /* Must be super user */
     581         114 :     if (!superuser())
     582          14 :         ereport(ERROR,
     583             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     584             :                  errmsg("permission denied to create foreign-data wrapper \"%s\"",
     585             :                         stmt->fdwname),
     586             :                  errhint("Must be superuser to create a foreign-data wrapper.")));
     587             : 
     588             :     /* For now the owner cannot be specified on create. Use effective user ID. */
     589         100 :     ownerId = GetUserId();
     590             : 
     591             :     /*
     592             :      * Check that there is no other foreign-data wrapper by this name.
     593             :      */
     594         100 :     if (GetForeignDataWrapperByName(stmt->fdwname, true) != NULL)
     595           4 :         ereport(ERROR,
     596             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     597             :                  errmsg("foreign-data wrapper \"%s\" already exists",
     598             :                         stmt->fdwname)));
     599             : 
     600             :     /*
     601             :      * Insert tuple into pg_foreign_data_wrapper.
     602             :      */
     603          96 :     memset(values, 0, sizeof(values));
     604          96 :     memset(nulls, false, sizeof(nulls));
     605             : 
     606          96 :     fdwId = GetNewOidWithIndex(rel, ForeignDataWrapperOidIndexId,
     607             :                                Anum_pg_foreign_data_wrapper_oid);
     608          96 :     values[Anum_pg_foreign_data_wrapper_oid - 1] = ObjectIdGetDatum(fdwId);
     609          96 :     values[Anum_pg_foreign_data_wrapper_fdwname - 1] =
     610          96 :         DirectFunctionCall1(namein, CStringGetDatum(stmt->fdwname));
     611          96 :     values[Anum_pg_foreign_data_wrapper_fdwowner - 1] = ObjectIdGetDatum(ownerId);
     612             : 
     613             :     /* Lookup handler and validator functions, if given */
     614          96 :     parse_func_options(stmt->func_options,
     615             :                        &handler_given, &fdwhandler,
     616             :                        &validator_given, &fdwvalidator);
     617             : 
     618          84 :     values[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
     619          84 :     values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
     620             : 
     621          84 :     nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
     622             : 
     623          84 :     fdwoptions = transformGenericOptions(ForeignDataWrapperRelationId,
     624             :                                          PointerGetDatum(NULL),
     625             :                                          stmt->options,
     626             :                                          fdwvalidator);
     627             : 
     628          80 :     if (PointerIsValid(DatumGetPointer(fdwoptions)))
     629          12 :         values[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = fdwoptions;
     630             :     else
     631          68 :         nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
     632             : 
     633          80 :     tuple = heap_form_tuple(rel->rd_att, values, nulls);
     634             : 
     635          80 :     CatalogTupleInsert(rel, tuple);
     636             : 
     637          80 :     heap_freetuple(tuple);
     638             : 
     639             :     /* record dependencies */
     640          80 :     myself.classId = ForeignDataWrapperRelationId;
     641          80 :     myself.objectId = fdwId;
     642          80 :     myself.objectSubId = 0;
     643             : 
     644          80 :     if (OidIsValid(fdwhandler))
     645             :     {
     646           8 :         referenced.classId = ProcedureRelationId;
     647           8 :         referenced.objectId = fdwhandler;
     648           8 :         referenced.objectSubId = 0;
     649           8 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     650             :     }
     651             : 
     652          80 :     if (OidIsValid(fdwvalidator))
     653             :     {
     654          18 :         referenced.classId = ProcedureRelationId;
     655          18 :         referenced.objectId = fdwvalidator;
     656          18 :         referenced.objectSubId = 0;
     657          18 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     658             :     }
     659             : 
     660          80 :     recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId);
     661             : 
     662             :     /* dependency on extension */
     663          80 :     recordDependencyOnCurrentExtension(&myself, false);
     664             : 
     665             :     /* Post creation hook for new foreign data wrapper */
     666          80 :     InvokeObjectPostCreateHook(ForeignDataWrapperRelationId, fdwId, 0);
     667             : 
     668          80 :     table_close(rel, RowExclusiveLock);
     669             : 
     670          80 :     return myself;
     671             : }
     672             : 
     673             : 
     674             : /*
     675             :  * Alter foreign-data wrapper
     676             :  */
     677             : ObjectAddress
     678          76 : AlterForeignDataWrapper(AlterFdwStmt *stmt)
     679             : {
     680             :     Relation    rel;
     681             :     HeapTuple   tp;
     682             :     Form_pg_foreign_data_wrapper fdwForm;
     683             :     Datum       repl_val[Natts_pg_foreign_data_wrapper];
     684             :     bool        repl_null[Natts_pg_foreign_data_wrapper];
     685             :     bool        repl_repl[Natts_pg_foreign_data_wrapper];
     686             :     Oid         fdwId;
     687             :     bool        isnull;
     688             :     Datum       datum;
     689             :     bool        handler_given;
     690             :     bool        validator_given;
     691             :     Oid         fdwhandler;
     692             :     Oid         fdwvalidator;
     693             :     ObjectAddress myself;
     694             : 
     695          76 :     rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
     696             : 
     697             :     /* Must be super user */
     698          76 :     if (!superuser())
     699          16 :         ereport(ERROR,
     700             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     701             :                  errmsg("permission denied to alter foreign-data wrapper \"%s\"",
     702             :                         stmt->fdwname),
     703             :                  errhint("Must be superuser to alter a foreign-data wrapper.")));
     704             : 
     705          60 :     tp = SearchSysCacheCopy1(FOREIGNDATAWRAPPERNAME,
     706             :                              CStringGetDatum(stmt->fdwname));
     707             : 
     708          60 :     if (!HeapTupleIsValid(tp))
     709           0 :         ereport(ERROR,
     710             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     711             :                  errmsg("foreign-data wrapper \"%s\" does not exist", stmt->fdwname)));
     712             : 
     713          60 :     fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
     714          60 :     fdwId = fdwForm->oid;
     715             : 
     716          60 :     memset(repl_val, 0, sizeof(repl_val));
     717          60 :     memset(repl_null, false, sizeof(repl_null));
     718          60 :     memset(repl_repl, false, sizeof(repl_repl));
     719             : 
     720          60 :     parse_func_options(stmt->func_options,
     721             :                        &handler_given, &fdwhandler,
     722             :                        &validator_given, &fdwvalidator);
     723             : 
     724          48 :     if (handler_given)
     725             :     {
     726           4 :         repl_val[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = ObjectIdGetDatum(fdwhandler);
     727           4 :         repl_repl[Anum_pg_foreign_data_wrapper_fdwhandler - 1] = true;
     728             : 
     729             :         /*
     730             :          * It could be that the behavior of accessing foreign table changes
     731             :          * with the new handler.  Warn about this.
     732             :          */
     733           4 :         ereport(WARNING,
     734             :                 (errmsg("changing the foreign-data wrapper handler can change behavior of existing foreign tables")));
     735             :     }
     736             : 
     737          48 :     if (validator_given)
     738             :     {
     739           8 :         repl_val[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = ObjectIdGetDatum(fdwvalidator);
     740           8 :         repl_repl[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = true;
     741             : 
     742             :         /*
     743             :          * It could be that existing options for the FDW or dependent SERVER,
     744             :          * USER MAPPING or FOREIGN TABLE objects are no longer valid according
     745             :          * to the new validator.  Warn about this.
     746             :          */
     747           8 :         if (OidIsValid(fdwvalidator))
     748           4 :             ereport(WARNING,
     749             :                     (errmsg("changing the foreign-data wrapper validator can cause "
     750             :                             "the options for dependent objects to become invalid")));
     751             :     }
     752             :     else
     753             :     {
     754             :         /*
     755             :          * Validator is not changed, but we need it for validating options.
     756             :          */
     757          40 :         fdwvalidator = fdwForm->fdwvalidator;
     758             :     }
     759             : 
     760             :     /*
     761             :      * If options specified, validate and update.
     762             :      */
     763          48 :     if (stmt->options)
     764             :     {
     765             :         /* Extract the current options */
     766          36 :         datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
     767             :                                 tp,
     768             :                                 Anum_pg_foreign_data_wrapper_fdwoptions,
     769             :                                 &isnull);
     770          36 :         if (isnull)
     771           8 :             datum = PointerGetDatum(NULL);
     772             : 
     773             :         /* Transform the options */
     774          36 :         datum = transformGenericOptions(ForeignDataWrapperRelationId,
     775             :                                         datum,
     776             :                                         stmt->options,
     777             :                                         fdwvalidator);
     778             : 
     779          20 :         if (PointerIsValid(DatumGetPointer(datum)))
     780          20 :             repl_val[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = datum;
     781             :         else
     782           0 :             repl_null[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
     783             : 
     784          20 :         repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
     785             :     }
     786             : 
     787             :     /* Everything looks good - update the tuple */
     788          32 :     tp = heap_modify_tuple(tp, RelationGetDescr(rel),
     789             :                            repl_val, repl_null, repl_repl);
     790             : 
     791          32 :     CatalogTupleUpdate(rel, &tp->t_self, tp);
     792             : 
     793          32 :     heap_freetuple(tp);
     794             : 
     795          32 :     ObjectAddressSet(myself, ForeignDataWrapperRelationId, fdwId);
     796             : 
     797             :     /* Update function dependencies if we changed them */
     798          32 :     if (handler_given || validator_given)
     799             :     {
     800             :         ObjectAddress referenced;
     801             : 
     802             :         /*
     803             :          * Flush all existing dependency records of this FDW on functions; we
     804             :          * assume there can be none other than the ones we are fixing.
     805             :          */
     806          12 :         deleteDependencyRecordsForClass(ForeignDataWrapperRelationId,
     807             :                                         fdwId,
     808             :                                         ProcedureRelationId,
     809             :                                         DEPENDENCY_NORMAL);
     810             : 
     811             :         /* And build new ones. */
     812             : 
     813          12 :         if (OidIsValid(fdwhandler))
     814             :         {
     815           4 :             referenced.classId = ProcedureRelationId;
     816           4 :             referenced.objectId = fdwhandler;
     817           4 :             referenced.objectSubId = 0;
     818           4 :             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     819             :         }
     820             : 
     821          12 :         if (OidIsValid(fdwvalidator))
     822             :         {
     823           4 :             referenced.classId = ProcedureRelationId;
     824           4 :             referenced.objectId = fdwvalidator;
     825           4 :             referenced.objectSubId = 0;
     826           4 :             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     827             :         }
     828             :     }
     829             : 
     830          32 :     InvokeObjectPostAlterHook(ForeignDataWrapperRelationId, fdwId, 0);
     831             : 
     832          32 :     table_close(rel, RowExclusiveLock);
     833             : 
     834          32 :     return myself;
     835             : }
     836             : 
     837             : 
     838             : /*
     839             :  * Drop foreign-data wrapper by OID
     840             :  */
     841             : void
     842          60 : RemoveForeignDataWrapperById(Oid fdwId)
     843             : {
     844             :     HeapTuple   tp;
     845             :     Relation    rel;
     846             : 
     847          60 :     rel = table_open(ForeignDataWrapperRelationId, RowExclusiveLock);
     848             : 
     849          60 :     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwId));
     850             : 
     851          60 :     if (!HeapTupleIsValid(tp))
     852           0 :         elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwId);
     853             : 
     854          60 :     CatalogTupleDelete(rel, &tp->t_self);
     855             : 
     856          60 :     ReleaseSysCache(tp);
     857             : 
     858          60 :     table_close(rel, RowExclusiveLock);
     859          60 : }
     860             : 
     861             : 
     862             : /*
     863             :  * Create a foreign server
     864             :  */
     865             : ObjectAddress
     866         170 : CreateForeignServer(CreateForeignServerStmt *stmt)
     867             : {
     868             :     Relation    rel;
     869             :     Datum       srvoptions;
     870             :     Datum       values[Natts_pg_foreign_server];
     871             :     bool        nulls[Natts_pg_foreign_server];
     872             :     HeapTuple   tuple;
     873             :     Oid         srvId;
     874             :     Oid         ownerId;
     875             :     AclResult   aclresult;
     876             :     ObjectAddress myself;
     877             :     ObjectAddress referenced;
     878             :     ForeignDataWrapper *fdw;
     879             : 
     880         170 :     rel = table_open(ForeignServerRelationId, RowExclusiveLock);
     881             : 
     882             :     /* For now the owner cannot be specified on create. Use effective user ID. */
     883         170 :     ownerId = GetUserId();
     884             : 
     885             :     /*
     886             :      * Check that there is no other foreign server by this name. Do nothing if
     887             :      * IF NOT EXISTS was enforced.
     888             :      */
     889         170 :     if (GetForeignServerByName(stmt->servername, true) != NULL)
     890             :     {
     891           8 :         if (stmt->if_not_exists)
     892             :         {
     893           4 :             ereport(NOTICE,
     894             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     895             :                      errmsg("server \"%s\" already exists, skipping",
     896             :                             stmt->servername)));
     897           4 :             table_close(rel, RowExclusiveLock);
     898           4 :             return InvalidObjectAddress;
     899             :         }
     900             :         else
     901           4 :             ereport(ERROR,
     902             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     903             :                      errmsg("server \"%s\" already exists",
     904             :                             stmt->servername)));
     905             :     }
     906             : 
     907             :     /*
     908             :      * Check that the FDW exists and that we have USAGE on it. Also get the
     909             :      * actual FDW for option validation etc.
     910             :      */
     911         162 :     fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
     912             : 
     913         158 :     aclresult = pg_foreign_data_wrapper_aclcheck(fdw->fdwid, ownerId, ACL_USAGE);
     914         158 :     if (aclresult != ACLCHECK_OK)
     915          18 :         aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
     916             : 
     917             :     /*
     918             :      * Insert tuple into pg_foreign_server.
     919             :      */
     920         140 :     memset(values, 0, sizeof(values));
     921         140 :     memset(nulls, false, sizeof(nulls));
     922             : 
     923         140 :     srvId = GetNewOidWithIndex(rel, ForeignServerOidIndexId,
     924             :                                Anum_pg_foreign_server_oid);
     925         140 :     values[Anum_pg_foreign_server_oid - 1] = ObjectIdGetDatum(srvId);
     926         140 :     values[Anum_pg_foreign_server_srvname - 1] =
     927         140 :         DirectFunctionCall1(namein, CStringGetDatum(stmt->servername));
     928         140 :     values[Anum_pg_foreign_server_srvowner - 1] = ObjectIdGetDatum(ownerId);
     929         140 :     values[Anum_pg_foreign_server_srvfdw - 1] = ObjectIdGetDatum(fdw->fdwid);
     930             : 
     931             :     /* Add server type if supplied */
     932         140 :     if (stmt->servertype)
     933          12 :         values[Anum_pg_foreign_server_srvtype - 1] =
     934          12 :             CStringGetTextDatum(stmt->servertype);
     935             :     else
     936         128 :         nulls[Anum_pg_foreign_server_srvtype - 1] = true;
     937             : 
     938             :     /* Add server version if supplied */
     939         140 :     if (stmt->version)
     940          12 :         values[Anum_pg_foreign_server_srvversion - 1] =
     941          12 :             CStringGetTextDatum(stmt->version);
     942             :     else
     943         128 :         nulls[Anum_pg_foreign_server_srvversion - 1] = true;
     944             : 
     945             :     /* Start with a blank acl */
     946         140 :     nulls[Anum_pg_foreign_server_srvacl - 1] = true;
     947             : 
     948             :     /* Add server options */
     949         140 :     srvoptions = transformGenericOptions(ForeignServerRelationId,
     950             :                                          PointerGetDatum(NULL),
     951             :                                          stmt->options,
     952             :                                          fdw->fdwvalidator);
     953             : 
     954         136 :     if (PointerIsValid(DatumGetPointer(srvoptions)))
     955          30 :         values[Anum_pg_foreign_server_srvoptions - 1] = srvoptions;
     956             :     else
     957         106 :         nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
     958             : 
     959         136 :     tuple = heap_form_tuple(rel->rd_att, values, nulls);
     960             : 
     961         136 :     CatalogTupleInsert(rel, tuple);
     962             : 
     963         136 :     heap_freetuple(tuple);
     964             : 
     965             :     /* record dependencies */
     966         136 :     myself.classId = ForeignServerRelationId;
     967         136 :     myself.objectId = srvId;
     968         136 :     myself.objectSubId = 0;
     969             : 
     970         136 :     referenced.classId = ForeignDataWrapperRelationId;
     971         136 :     referenced.objectId = fdw->fdwid;
     972         136 :     referenced.objectSubId = 0;
     973         136 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     974             : 
     975         136 :     recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
     976             : 
     977             :     /* dependency on extension */
     978         136 :     recordDependencyOnCurrentExtension(&myself, false);
     979             : 
     980             :     /* Post creation hook for new foreign server */
     981         136 :     InvokeObjectPostCreateHook(ForeignServerRelationId, srvId, 0);
     982             : 
     983         136 :     table_close(rel, RowExclusiveLock);
     984             : 
     985         136 :     return myself;
     986             : }
     987             : 
     988             : 
     989             : /*
     990             :  * Alter foreign server
     991             :  */
     992             : ObjectAddress
     993          94 : AlterForeignServer(AlterForeignServerStmt *stmt)
     994             : {
     995             :     Relation    rel;
     996             :     HeapTuple   tp;
     997             :     Datum       repl_val[Natts_pg_foreign_server];
     998             :     bool        repl_null[Natts_pg_foreign_server];
     999             :     bool        repl_repl[Natts_pg_foreign_server];
    1000             :     Oid         srvId;
    1001             :     Form_pg_foreign_server srvForm;
    1002             :     ObjectAddress address;
    1003             : 
    1004          94 :     rel = table_open(ForeignServerRelationId, RowExclusiveLock);
    1005             : 
    1006          94 :     tp = SearchSysCacheCopy1(FOREIGNSERVERNAME,
    1007             :                              CStringGetDatum(stmt->servername));
    1008             : 
    1009          94 :     if (!HeapTupleIsValid(tp))
    1010           4 :         ereport(ERROR,
    1011             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1012             :                  errmsg("server \"%s\" does not exist", stmt->servername)));
    1013             : 
    1014          90 :     srvForm = (Form_pg_foreign_server) GETSTRUCT(tp);
    1015          90 :     srvId = srvForm->oid;
    1016             : 
    1017             :     /*
    1018             :      * Only owner or a superuser can ALTER a SERVER.
    1019             :      */
    1020          90 :     if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
    1021          16 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
    1022          16 :                        stmt->servername);
    1023             : 
    1024          74 :     memset(repl_val, 0, sizeof(repl_val));
    1025          74 :     memset(repl_null, false, sizeof(repl_null));
    1026          74 :     memset(repl_repl, false, sizeof(repl_repl));
    1027             : 
    1028          74 :     if (stmt->has_version)
    1029             :     {
    1030             :         /*
    1031             :          * Change the server VERSION string.
    1032             :          */
    1033          16 :         if (stmt->version)
    1034          16 :             repl_val[Anum_pg_foreign_server_srvversion - 1] =
    1035          16 :                 CStringGetTextDatum(stmt->version);
    1036             :         else
    1037           0 :             repl_null[Anum_pg_foreign_server_srvversion - 1] = true;
    1038             : 
    1039          16 :         repl_repl[Anum_pg_foreign_server_srvversion - 1] = true;
    1040             :     }
    1041             : 
    1042          74 :     if (stmt->options)
    1043             :     {
    1044          62 :         ForeignDataWrapper *fdw = GetForeignDataWrapper(srvForm->srvfdw);
    1045             :         Datum       datum;
    1046             :         bool        isnull;
    1047             : 
    1048             :         /* Extract the current srvoptions */
    1049          62 :         datum = SysCacheGetAttr(FOREIGNSERVEROID,
    1050             :                                 tp,
    1051             :                                 Anum_pg_foreign_server_srvoptions,
    1052             :                                 &isnull);
    1053          62 :         if (isnull)
    1054          14 :             datum = PointerGetDatum(NULL);
    1055             : 
    1056             :         /* Prepare the options array */
    1057          62 :         datum = transformGenericOptions(ForeignServerRelationId,
    1058             :                                         datum,
    1059             :                                         stmt->options,
    1060             :                                         fdw->fdwvalidator);
    1061             : 
    1062          50 :         if (PointerIsValid(DatumGetPointer(datum)))
    1063          46 :             repl_val[Anum_pg_foreign_server_srvoptions - 1] = datum;
    1064             :         else
    1065           4 :             repl_null[Anum_pg_foreign_server_srvoptions - 1] = true;
    1066             : 
    1067          50 :         repl_repl[Anum_pg_foreign_server_srvoptions - 1] = true;
    1068             :     }
    1069             : 
    1070             :     /* Everything looks good - update the tuple */
    1071          62 :     tp = heap_modify_tuple(tp, RelationGetDescr(rel),
    1072             :                            repl_val, repl_null, repl_repl);
    1073             : 
    1074          62 :     CatalogTupleUpdate(rel, &tp->t_self, tp);
    1075             : 
    1076          62 :     InvokeObjectPostAlterHook(ForeignServerRelationId, srvId, 0);
    1077             : 
    1078          62 :     ObjectAddressSet(address, ForeignServerRelationId, srvId);
    1079             : 
    1080          62 :     heap_freetuple(tp);
    1081             : 
    1082          62 :     table_close(rel, RowExclusiveLock);
    1083             : 
    1084          62 :     return address;
    1085             : }
    1086             : 
    1087             : 
    1088             : /*
    1089             :  * Drop foreign server by OID
    1090             :  */
    1091             : void
    1092         108 : RemoveForeignServerById(Oid srvId)
    1093             : {
    1094             :     HeapTuple   tp;
    1095             :     Relation    rel;
    1096             : 
    1097         108 :     rel = table_open(ForeignServerRelationId, RowExclusiveLock);
    1098             : 
    1099         108 :     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(srvId));
    1100             : 
    1101         108 :     if (!HeapTupleIsValid(tp))
    1102           0 :         elog(ERROR, "cache lookup failed for foreign server %u", srvId);
    1103             : 
    1104         108 :     CatalogTupleDelete(rel, &tp->t_self);
    1105             : 
    1106         108 :     ReleaseSysCache(tp);
    1107             : 
    1108         108 :     table_close(rel, RowExclusiveLock);
    1109         108 : }
    1110             : 
    1111             : 
    1112             : /*
    1113             :  * Common routine to check permission for user-mapping-related DDL
    1114             :  * commands.  We allow server owners to operate on any mapping, and
    1115             :  * users to operate on their own mapping.
    1116             :  */
    1117             : static void
    1118         298 : user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
    1119             : {
    1120         298 :     Oid         curuserid = GetUserId();
    1121             : 
    1122         298 :     if (!pg_foreign_server_ownercheck(serverid, curuserid))
    1123             :     {
    1124          50 :         if (umuserid == curuserid)
    1125             :         {
    1126             :             AclResult   aclresult;
    1127             : 
    1128          14 :             aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE);
    1129          14 :             if (aclresult != ACLCHECK_OK)
    1130           6 :                 aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
    1131             :         }
    1132             :         else
    1133          36 :             aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FOREIGN_SERVER,
    1134             :                            servername);
    1135             :     }
    1136         256 : }
    1137             : 
    1138             : 
    1139             : /*
    1140             :  * Create user mapping
    1141             :  */
    1142             : ObjectAddress
    1143         172 : CreateUserMapping(CreateUserMappingStmt *stmt)
    1144             : {
    1145             :     Relation    rel;
    1146             :     Datum       useoptions;
    1147             :     Datum       values[Natts_pg_user_mapping];
    1148             :     bool        nulls[Natts_pg_user_mapping];
    1149             :     HeapTuple   tuple;
    1150             :     Oid         useId;
    1151             :     Oid         umId;
    1152             :     ObjectAddress myself;
    1153             :     ObjectAddress referenced;
    1154             :     ForeignServer *srv;
    1155             :     ForeignDataWrapper *fdw;
    1156         172 :     RoleSpec   *role = (RoleSpec *) stmt->user;
    1157             : 
    1158         172 :     rel = table_open(UserMappingRelationId, RowExclusiveLock);
    1159             : 
    1160         172 :     if (role->roletype == ROLESPEC_PUBLIC)
    1161          48 :         useId = ACL_ID_PUBLIC;
    1162             :     else
    1163         124 :         useId = get_rolespec_oid(stmt->user, false);
    1164             : 
    1165             :     /* Check that the server exists. */
    1166         166 :     srv = GetForeignServerByName(stmt->servername, false);
    1167             : 
    1168         162 :     user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
    1169             : 
    1170             :     /*
    1171             :      * Check that the user mapping is unique within server.
    1172             :      */
    1173         144 :     umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
    1174             :                            ObjectIdGetDatum(useId),
    1175             :                            ObjectIdGetDatum(srv->serverid));
    1176             : 
    1177         144 :     if (OidIsValid(umId))
    1178             :     {
    1179          12 :         if (stmt->if_not_exists)
    1180             :         {
    1181           4 :             ereport(NOTICE,
    1182             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
    1183             :                      errmsg("user mapping for \"%s\" already exists for server \"%s\", skipping",
    1184             :                             MappingUserName(useId),
    1185             :                             stmt->servername)));
    1186             : 
    1187           4 :             table_close(rel, RowExclusiveLock);
    1188           4 :             return InvalidObjectAddress;
    1189             :         }
    1190             :         else
    1191           8 :             ereport(ERROR,
    1192             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
    1193             :                      errmsg("user mapping for \"%s\" already exists for server \"%s\"",
    1194             :                             MappingUserName(useId),
    1195             :                             stmt->servername)));
    1196             :     }
    1197             : 
    1198         132 :     fdw = GetForeignDataWrapper(srv->fdwid);
    1199             : 
    1200             :     /*
    1201             :      * Insert tuple into pg_user_mapping.
    1202             :      */
    1203         132 :     memset(values, 0, sizeof(values));
    1204         132 :     memset(nulls, false, sizeof(nulls));
    1205             : 
    1206         132 :     umId = GetNewOidWithIndex(rel, UserMappingOidIndexId,
    1207             :                               Anum_pg_user_mapping_oid);
    1208         132 :     values[Anum_pg_user_mapping_oid - 1] = ObjectIdGetDatum(umId);
    1209         132 :     values[Anum_pg_user_mapping_umuser - 1] = ObjectIdGetDatum(useId);
    1210         132 :     values[Anum_pg_user_mapping_umserver - 1] = ObjectIdGetDatum(srv->serverid);
    1211             : 
    1212             :     /* Add user options */
    1213         132 :     useoptions = transformGenericOptions(UserMappingRelationId,
    1214             :                                          PointerGetDatum(NULL),
    1215             :                                          stmt->options,
    1216             :                                          fdw->fdwvalidator);
    1217             : 
    1218         122 :     if (PointerIsValid(DatumGetPointer(useoptions)))
    1219          60 :         values[Anum_pg_user_mapping_umoptions - 1] = useoptions;
    1220             :     else
    1221          62 :         nulls[Anum_pg_user_mapping_umoptions - 1] = true;
    1222             : 
    1223         122 :     tuple = heap_form_tuple(rel->rd_att, values, nulls);
    1224             : 
    1225         122 :     CatalogTupleInsert(rel, tuple);
    1226             : 
    1227         122 :     heap_freetuple(tuple);
    1228             : 
    1229             :     /* Add dependency on the server */
    1230         122 :     myself.classId = UserMappingRelationId;
    1231         122 :     myself.objectId = umId;
    1232         122 :     myself.objectSubId = 0;
    1233             : 
    1234         122 :     referenced.classId = ForeignServerRelationId;
    1235         122 :     referenced.objectId = srv->serverid;
    1236         122 :     referenced.objectSubId = 0;
    1237         122 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    1238             : 
    1239         122 :     if (OidIsValid(useId))
    1240             :     {
    1241             :         /* Record the mapped user dependency */
    1242          92 :         recordDependencyOnOwner(UserMappingRelationId, umId, useId);
    1243             :     }
    1244             : 
    1245             :     /*
    1246             :      * Perhaps someday there should be a recordDependencyOnCurrentExtension
    1247             :      * call here; but since roles aren't members of extensions, it seems like
    1248             :      * user mappings shouldn't be either.  Note that the grammar and pg_dump
    1249             :      * would need to be extended too if we change this.
    1250             :      */
    1251             : 
    1252             :     /* Post creation hook for new user mapping */
    1253         122 :     InvokeObjectPostCreateHook(UserMappingRelationId, umId, 0);
    1254             : 
    1255         122 :     table_close(rel, RowExclusiveLock);
    1256             : 
    1257         122 :     return myself;
    1258             : }
    1259             : 
    1260             : 
    1261             : /*
    1262             :  * Alter user mapping
    1263             :  */
    1264             : ObjectAddress
    1265          86 : AlterUserMapping(AlterUserMappingStmt *stmt)
    1266             : {
    1267             :     Relation    rel;
    1268             :     HeapTuple   tp;
    1269             :     Datum       repl_val[Natts_pg_user_mapping];
    1270             :     bool        repl_null[Natts_pg_user_mapping];
    1271             :     bool        repl_repl[Natts_pg_user_mapping];
    1272             :     Oid         useId;
    1273             :     Oid         umId;
    1274             :     ForeignServer *srv;
    1275             :     ObjectAddress address;
    1276          86 :     RoleSpec   *role = (RoleSpec *) stmt->user;
    1277             : 
    1278          86 :     rel = table_open(UserMappingRelationId, RowExclusiveLock);
    1279             : 
    1280          86 :     if (role->roletype == ROLESPEC_PUBLIC)
    1281          22 :         useId = ACL_ID_PUBLIC;
    1282             :     else
    1283          64 :         useId = get_rolespec_oid(stmt->user, false);
    1284             : 
    1285          80 :     srv = GetForeignServerByName(stmt->servername, false);
    1286             : 
    1287          76 :     umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
    1288             :                            ObjectIdGetDatum(useId),
    1289             :                            ObjectIdGetDatum(srv->serverid));
    1290          76 :     if (!OidIsValid(umId))
    1291           4 :         ereport(ERROR,
    1292             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1293             :                  errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
    1294             :                         MappingUserName(useId), stmt->servername)));
    1295             : 
    1296          72 :     user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
    1297             : 
    1298          60 :     tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
    1299             : 
    1300          60 :     if (!HeapTupleIsValid(tp))
    1301           0 :         elog(ERROR, "cache lookup failed for user mapping %u", umId);
    1302             : 
    1303          60 :     memset(repl_val, 0, sizeof(repl_val));
    1304          60 :     memset(repl_null, false, sizeof(repl_null));
    1305          60 :     memset(repl_repl, false, sizeof(repl_repl));
    1306             : 
    1307          60 :     if (stmt->options)
    1308             :     {
    1309             :         ForeignDataWrapper *fdw;
    1310             :         Datum       datum;
    1311             :         bool        isnull;
    1312             : 
    1313             :         /*
    1314             :          * Process the options.
    1315             :          */
    1316             : 
    1317          60 :         fdw = GetForeignDataWrapper(srv->fdwid);
    1318             : 
    1319          60 :         datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
    1320             :                                 tp,
    1321             :                                 Anum_pg_user_mapping_umoptions,
    1322             :                                 &isnull);
    1323          60 :         if (isnull)
    1324          16 :             datum = PointerGetDatum(NULL);
    1325             : 
    1326             :         /* Prepare the options array */
    1327          60 :         datum = transformGenericOptions(UserMappingRelationId,
    1328             :                                         datum,
    1329             :                                         stmt->options,
    1330             :                                         fdw->fdwvalidator);
    1331             : 
    1332          48 :         if (PointerIsValid(DatumGetPointer(datum)))
    1333          40 :             repl_val[Anum_pg_user_mapping_umoptions - 1] = datum;
    1334             :         else
    1335           8 :             repl_null[Anum_pg_user_mapping_umoptions - 1] = true;
    1336             : 
    1337          48 :         repl_repl[Anum_pg_user_mapping_umoptions - 1] = true;
    1338             :     }
    1339             : 
    1340             :     /* Everything looks good - update the tuple */
    1341          48 :     tp = heap_modify_tuple(tp, RelationGetDescr(rel),
    1342             :                            repl_val, repl_null, repl_repl);
    1343             : 
    1344          48 :     CatalogTupleUpdate(rel, &tp->t_self, tp);
    1345             : 
    1346          48 :     InvokeObjectPostAlterHook(UserMappingRelationId,
    1347             :                               umId, 0);
    1348             : 
    1349          48 :     ObjectAddressSet(address, UserMappingRelationId, umId);
    1350             : 
    1351          48 :     heap_freetuple(tp);
    1352             : 
    1353          48 :     table_close(rel, RowExclusiveLock);
    1354             : 
    1355          48 :     return address;
    1356             : }
    1357             : 
    1358             : 
    1359             : /*
    1360             :  * Drop user mapping
    1361             :  */
    1362             : Oid
    1363          92 : RemoveUserMapping(DropUserMappingStmt *stmt)
    1364             : {
    1365             :     ObjectAddress object;
    1366             :     Oid         useId;
    1367             :     Oid         umId;
    1368             :     ForeignServer *srv;
    1369          92 :     RoleSpec   *role = (RoleSpec *) stmt->user;
    1370             : 
    1371          92 :     if (role->roletype == ROLESPEC_PUBLIC)
    1372          24 :         useId = ACL_ID_PUBLIC;
    1373             :     else
    1374             :     {
    1375          68 :         useId = get_rolespec_oid(stmt->user, stmt->missing_ok);
    1376          62 :         if (!OidIsValid(useId))
    1377             :         {
    1378             :             /*
    1379             :              * IF EXISTS specified, role not found and not public. Notice this
    1380             :              * and leave.
    1381             :              */
    1382           6 :             elog(NOTICE, "role \"%s\" does not exist, skipping",
    1383             :                  role->rolename);
    1384           6 :             return InvalidOid;
    1385             :         }
    1386             :     }
    1387             : 
    1388          80 :     srv = GetForeignServerByName(stmt->servername, true);
    1389             : 
    1390          80 :     if (!srv)
    1391             :     {
    1392           8 :         if (!stmt->missing_ok)
    1393           4 :             ereport(ERROR,
    1394             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    1395             :                      errmsg("server \"%s\" does not exist",
    1396             :                             stmt->servername)));
    1397             :         /* IF EXISTS, just note it */
    1398           4 :         ereport(NOTICE,
    1399             :                 (errmsg("server \"%s\" does not exist, skipping",
    1400             :                         stmt->servername)));
    1401           4 :         return InvalidOid;
    1402             :     }
    1403             : 
    1404          72 :     umId = GetSysCacheOid2(USERMAPPINGUSERSERVER, Anum_pg_user_mapping_oid,
    1405             :                            ObjectIdGetDatum(useId),
    1406             :                            ObjectIdGetDatum(srv->serverid));
    1407             : 
    1408          72 :     if (!OidIsValid(umId))
    1409             :     {
    1410           8 :         if (!stmt->missing_ok)
    1411           4 :             ereport(ERROR,
    1412             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    1413             :                      errmsg("user mapping for \"%s\" does not exist for server \"%s\"",
    1414             :                             MappingUserName(useId), stmt->servername)));
    1415             : 
    1416             :         /* IF EXISTS specified, just note it */
    1417           4 :         ereport(NOTICE,
    1418             :                 (errmsg("user mapping for \"%s\" does not exist for server \"%s\", skipping",
    1419             :                         MappingUserName(useId), stmt->servername)));
    1420           4 :         return InvalidOid;
    1421             :     }
    1422             : 
    1423          64 :     user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
    1424             : 
    1425             :     /*
    1426             :      * Do the deletion
    1427             :      */
    1428          52 :     object.classId = UserMappingRelationId;
    1429          52 :     object.objectId = umId;
    1430          52 :     object.objectSubId = 0;
    1431             : 
    1432          52 :     performDeletion(&object, DROP_CASCADE, 0);
    1433             : 
    1434          52 :     return umId;
    1435             : }
    1436             : 
    1437             : 
    1438             : /*
    1439             :  * Drop user mapping by OID.  This is called to clean up dependencies.
    1440             :  */
    1441             : void
    1442         114 : RemoveUserMappingById(Oid umId)
    1443             : {
    1444             :     HeapTuple   tp;
    1445             :     Relation    rel;
    1446             : 
    1447         114 :     rel = table_open(UserMappingRelationId, RowExclusiveLock);
    1448             : 
    1449         114 :     tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umId));
    1450             : 
    1451         114 :     if (!HeapTupleIsValid(tp))
    1452           0 :         elog(ERROR, "cache lookup failed for user mapping %u", umId);
    1453             : 
    1454         114 :     CatalogTupleDelete(rel, &tp->t_self);
    1455             : 
    1456         114 :     ReleaseSysCache(tp);
    1457             : 
    1458         114 :     table_close(rel, RowExclusiveLock);
    1459         114 : }
    1460             : 
    1461             : /*
    1462             :  * Create a foreign table
    1463             :  * call after DefineRelation().
    1464             :  */
    1465             : void
    1466         262 : CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
    1467             : {
    1468             :     Relation    ftrel;
    1469             :     Datum       ftoptions;
    1470             :     Datum       values[Natts_pg_foreign_table];
    1471             :     bool        nulls[Natts_pg_foreign_table];
    1472             :     HeapTuple   tuple;
    1473             :     AclResult   aclresult;
    1474             :     ObjectAddress myself;
    1475             :     ObjectAddress referenced;
    1476             :     Oid         ownerId;
    1477             :     ForeignDataWrapper *fdw;
    1478             :     ForeignServer *server;
    1479             : 
    1480             :     /*
    1481             :      * Advance command counter to ensure the pg_attribute tuple is visible;
    1482             :      * the tuple might be updated to add constraints in previous step.
    1483             :      */
    1484         262 :     CommandCounterIncrement();
    1485             : 
    1486         262 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
    1487             : 
    1488             :     /*
    1489             :      * For now the owner cannot be specified on create. Use effective user ID.
    1490             :      */
    1491         262 :     ownerId = GetUserId();
    1492             : 
    1493             :     /*
    1494             :      * Check that the foreign server exists and that we have USAGE on it. Also
    1495             :      * get the actual FDW for option validation etc.
    1496             :      */
    1497         262 :     server = GetForeignServerByName(stmt->servername, false);
    1498         258 :     aclresult = pg_foreign_server_aclcheck(server->serverid, ownerId, ACL_USAGE);
    1499         258 :     if (aclresult != ACLCHECK_OK)
    1500           0 :         aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
    1501             : 
    1502         258 :     fdw = GetForeignDataWrapper(server->fdwid);
    1503             : 
    1504             :     /*
    1505             :      * Insert tuple into pg_foreign_table.
    1506             :      */
    1507         258 :     memset(values, 0, sizeof(values));
    1508         258 :     memset(nulls, false, sizeof(nulls));
    1509             : 
    1510         258 :     values[Anum_pg_foreign_table_ftrelid - 1] = ObjectIdGetDatum(relid);
    1511         258 :     values[Anum_pg_foreign_table_ftserver - 1] = ObjectIdGetDatum(server->serverid);
    1512             :     /* Add table generic options */
    1513         258 :     ftoptions = transformGenericOptions(ForeignTableRelationId,
    1514             :                                         PointerGetDatum(NULL),
    1515             :                                         stmt->options,
    1516             :                                         fdw->fdwvalidator);
    1517             : 
    1518         210 :     if (PointerIsValid(DatumGetPointer(ftoptions)))
    1519         150 :         values[Anum_pg_foreign_table_ftoptions - 1] = ftoptions;
    1520             :     else
    1521          60 :         nulls[Anum_pg_foreign_table_ftoptions - 1] = true;
    1522             : 
    1523         210 :     tuple = heap_form_tuple(ftrel->rd_att, values, nulls);
    1524             : 
    1525         210 :     CatalogTupleInsert(ftrel, tuple);
    1526             : 
    1527         210 :     heap_freetuple(tuple);
    1528             : 
    1529             :     /* Add pg_class dependency on the server */
    1530         210 :     myself.classId = RelationRelationId;
    1531         210 :     myself.objectId = relid;
    1532         210 :     myself.objectSubId = 0;
    1533             : 
    1534         210 :     referenced.classId = ForeignServerRelationId;
    1535         210 :     referenced.objectId = server->serverid;
    1536         210 :     referenced.objectSubId = 0;
    1537         210 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    1538             : 
    1539         210 :     table_close(ftrel, RowExclusiveLock);
    1540         210 : }
    1541             : 
    1542             : /*
    1543             :  * Import a foreign schema
    1544             :  */
    1545             : void
    1546          36 : ImportForeignSchema(ImportForeignSchemaStmt *stmt)
    1547             : {
    1548             :     ForeignServer *server;
    1549             :     ForeignDataWrapper *fdw;
    1550             :     FdwRoutine *fdw_routine;
    1551             :     AclResult   aclresult;
    1552             :     List       *cmd_list;
    1553             :     ListCell   *lc;
    1554             : 
    1555             :     /* Check that the foreign server exists and that we have USAGE on it */
    1556          36 :     server = GetForeignServerByName(stmt->server_name, false);
    1557          34 :     aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE);
    1558          34 :     if (aclresult != ACLCHECK_OK)
    1559           0 :         aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
    1560             : 
    1561             :     /* Check that the schema exists and we have CREATE permissions on it */
    1562          34 :     (void) LookupCreationNamespace(stmt->local_schema);
    1563             : 
    1564             :     /* Get the FDW and check it supports IMPORT */
    1565          32 :     fdw = GetForeignDataWrapper(server->fdwid);
    1566          32 :     if (!OidIsValid(fdw->fdwhandler))
    1567          16 :         ereport(ERROR,
    1568             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1569             :                  errmsg("foreign-data wrapper \"%s\" has no handler",
    1570             :                         fdw->fdwname)));
    1571          16 :     fdw_routine = GetFdwRoutine(fdw->fdwhandler);
    1572          16 :     if (fdw_routine->ImportForeignSchema == NULL)
    1573           0 :         ereport(ERROR,
    1574             :                 (errcode(ERRCODE_FDW_NO_SCHEMAS),
    1575             :                  errmsg("foreign-data wrapper \"%s\" does not support IMPORT FOREIGN SCHEMA",
    1576             :                         fdw->fdwname)));
    1577             : 
    1578             :     /* Call FDW to get a list of commands */
    1579          16 :     cmd_list = fdw_routine->ImportForeignSchema(stmt, server->serverid);
    1580             : 
    1581             :     /* Parse and execute each command */
    1582          60 :     foreach(lc, cmd_list)
    1583             :     {
    1584          50 :         char       *cmd = (char *) lfirst(lc);
    1585             :         import_error_callback_arg callback_arg;
    1586             :         ErrorContextCallback sqlerrcontext;
    1587             :         List       *raw_parsetree_list;
    1588             :         ListCell   *lc2;
    1589             : 
    1590             :         /*
    1591             :          * Setup error traceback support for ereport().  This is so that any
    1592             :          * error in the generated SQL will be displayed nicely.
    1593             :          */
    1594          50 :         callback_arg.tablename = NULL;  /* not known yet */
    1595          50 :         callback_arg.cmd = cmd;
    1596          50 :         sqlerrcontext.callback = import_error_callback;
    1597          50 :         sqlerrcontext.arg = (void *) &callback_arg;
    1598          50 :         sqlerrcontext.previous = error_context_stack;
    1599          50 :         error_context_stack = &sqlerrcontext;
    1600             : 
    1601             :         /*
    1602             :          * Parse the SQL string into a list of raw parse trees.
    1603             :          */
    1604          50 :         raw_parsetree_list = pg_parse_query(cmd);
    1605             : 
    1606             :         /*
    1607             :          * Process each parse tree (we allow the FDW to put more than one
    1608             :          * command per string, though this isn't really advised).
    1609             :          */
    1610          96 :         foreach(lc2, raw_parsetree_list)
    1611             :         {
    1612          50 :             RawStmt    *rs = lfirst_node(RawStmt, lc2);
    1613          50 :             CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) rs->stmt;
    1614             :             PlannedStmt *pstmt;
    1615             : 
    1616             :             /*
    1617             :              * Because we only allow CreateForeignTableStmt, we can skip parse
    1618             :              * analysis, rewrite, and planning steps here.
    1619             :              */
    1620          50 :             if (!IsA(cstmt, CreateForeignTableStmt))
    1621           0 :                 elog(ERROR,
    1622             :                      "foreign-data wrapper \"%s\" returned incorrect statement type %d",
    1623             :                      fdw->fdwname, (int) nodeTag(cstmt));
    1624             : 
    1625             :             /* Ignore commands for tables excluded by filter options */
    1626          50 :             if (!IsImportableForeignTable(cstmt->base.relation->relname, stmt))
    1627           0 :                 continue;
    1628             : 
    1629             :             /* Enable reporting of current table's name on error */
    1630          50 :             callback_arg.tablename = cstmt->base.relation->relname;
    1631             : 
    1632             :             /* Ensure creation schema is the one given in IMPORT statement */
    1633          50 :             cstmt->base.relation->schemaname = pstrdup(stmt->local_schema);
    1634             : 
    1635             :             /* No planning needed, just make a wrapper PlannedStmt */
    1636          50 :             pstmt = makeNode(PlannedStmt);
    1637          50 :             pstmt->commandType = CMD_UTILITY;
    1638          50 :             pstmt->canSetTag = false;
    1639          50 :             pstmt->utilityStmt = (Node *) cstmt;
    1640          50 :             pstmt->stmt_location = rs->stmt_location;
    1641          50 :             pstmt->stmt_len = rs->stmt_len;
    1642             : 
    1643             :             /* Execute statement */
    1644          50 :             ProcessUtility(pstmt,
    1645             :                            cmd,
    1646             :                            PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
    1647             :                            None_Receiver, NULL);
    1648             : 
    1649             :             /* Be sure to advance the command counter between subcommands */
    1650          46 :             CommandCounterIncrement();
    1651             : 
    1652          46 :             callback_arg.tablename = NULL;
    1653             :         }
    1654             : 
    1655          46 :         error_context_stack = sqlerrcontext.previous;
    1656             :     }
    1657          10 : }
    1658             : 
    1659             : /*
    1660             :  * error context callback to let us supply the failing SQL statement's text
    1661             :  */
    1662             : static void
    1663           4 : import_error_callback(void *arg)
    1664             : {
    1665           4 :     import_error_callback_arg *callback_arg = (import_error_callback_arg *) arg;
    1666             :     int         syntaxerrposition;
    1667             : 
    1668             :     /* If it's a syntax error, convert to internal syntax error report */
    1669           4 :     syntaxerrposition = geterrposition();
    1670           4 :     if (syntaxerrposition > 0)
    1671             :     {
    1672           2 :         errposition(0);
    1673           2 :         internalerrposition(syntaxerrposition);
    1674           2 :         internalerrquery(callback_arg->cmd);
    1675             :     }
    1676             : 
    1677           4 :     if (callback_arg->tablename)
    1678           4 :         errcontext("importing foreign table \"%s\"",
    1679             :                    callback_arg->tablename);
    1680           4 : }

Generated by: LCOV version 1.13