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

Generated by: LCOV version 1.16