LCOV - code coverage report
Current view: top level - src/backend/commands - foreigncmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 93.8 % 582 546
Test Date: 2026-07-03 19:57:34 Functions: 95.7 % 23 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.2 % 340 249

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

Generated by: LCOV version 2.0-1