LCOV - code coverage report
Current view: top level - src/backend/commands - foreigncmds.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.4 % 757 669
Test Date: 2026-01-26 10:56:24 Functions: 90.9 % 22 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 61.0 % 385 235

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

Generated by: LCOV version 2.3.2-1