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

Generated by: LCOV version 2.0-1