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

Generated by: LCOV version 2.0-1