LCOV - code coverage report
Current view: top level - src/backend/commands - foreigncmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 464 556 83.5 %
Date: 2017-09-29 15:12:54 Functions: 22 25 88.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11