LCOV - code coverage report
Current view: top level - src/backend/foreign - foreign.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.0 % 261 235
Test Date: 2026-07-12 22:15:32 Functions: 100.0 % 22 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 67.6 % 182 123

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * foreign.c
       4                 :             :  *        support for foreign-data wrappers, servers and user mappings.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *        src/backend/foreign/foreign.c
      10                 :             :  *
      11                 :             :  *-------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : #include "postgres.h"
      14                 :             : 
      15                 :             : #include "access/htup_details.h"
      16                 :             : #include "access/reloptions.h"
      17                 :             : #include "catalog/pg_foreign_data_wrapper.h"
      18                 :             : #include "catalog/pg_foreign_server.h"
      19                 :             : #include "catalog/pg_foreign_table.h"
      20                 :             : #include "catalog/pg_user_mapping.h"
      21                 :             : #include "foreign/fdwapi.h"
      22                 :             : #include "foreign/foreign.h"
      23                 :             : #include "funcapi.h"
      24                 :             : #include "miscadmin.h"
      25                 :             : #include "optimizer/paths.h"
      26                 :             : #include "tcop/tcopprot.h"
      27                 :             : #include "utils/builtins.h"
      28                 :             : #include "utils/memutils.h"
      29                 :             : #include "utils/rel.h"
      30                 :             : #include "utils/syscache.h"
      31                 :             : #include "utils/tuplestore.h"
      32                 :             : #include "utils/varlena.h"
      33                 :             : 
      34                 :             : 
      35                 :             : /*
      36                 :             :  * GetForeignDataWrapper -  look up the foreign-data wrapper by OID.
      37                 :             :  */
      38                 :             : ForeignDataWrapper *
      39                 :         995 : GetForeignDataWrapper(Oid fdwid)
      40                 :             : {
      41                 :         995 :     return GetForeignDataWrapperExtended(fdwid, 0);
      42                 :             : }
      43                 :             : 
      44                 :             : 
      45                 :             : /*
      46                 :             :  * GetForeignDataWrapperExtended -  look up the foreign-data wrapper
      47                 :             :  * by OID. If flags uses FDW_MISSING_OK, return NULL if the object cannot
      48                 :             :  * be found instead of raising an error.
      49                 :             :  */
      50                 :             : ForeignDataWrapper *
      51                 :        1097 : GetForeignDataWrapperExtended(Oid fdwid, uint16 flags)
      52                 :             : {
      53                 :             :     Form_pg_foreign_data_wrapper fdwform;
      54                 :             :     ForeignDataWrapper *fdw;
      55                 :             :     Datum       datum;
      56                 :             :     HeapTuple   tp;
      57                 :             :     bool        isnull;
      58                 :             : 
      59                 :        1097 :     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
      60                 :             : 
      61         [ +  + ]:        1097 :     if (!HeapTupleIsValid(tp))
      62                 :             :     {
      63         [ -  + ]:          12 :         if ((flags & FDW_MISSING_OK) == 0)
      64         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
      65                 :          12 :         return NULL;
      66                 :             :     }
      67                 :             : 
      68                 :        1085 :     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
      69                 :             : 
      70                 :        1085 :     fdw = palloc_object(ForeignDataWrapper);
      71                 :        1085 :     fdw->fdwid = fdwid;
      72                 :        1085 :     fdw->owner = fdwform->fdwowner;
      73                 :        1085 :     fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
      74                 :        1085 :     fdw->fdwhandler = fdwform->fdwhandler;
      75                 :        1085 :     fdw->fdwvalidator = fdwform->fdwvalidator;
      76                 :        1085 :     fdw->fdwconnection = fdwform->fdwconnection;
      77                 :             : 
      78                 :             :     /* Extract the fdwoptions */
      79                 :        1085 :     datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
      80                 :             :                             tp,
      81                 :             :                             Anum_pg_foreign_data_wrapper_fdwoptions,
      82                 :             :                             &isnull);
      83         [ +  + ]:        1085 :     if (isnull)
      84                 :         917 :         fdw->options = NIL;
      85                 :             :     else
      86                 :         168 :         fdw->options = untransformRelOptions(datum);
      87                 :             : 
      88                 :        1085 :     ReleaseSysCache(tp);
      89                 :             : 
      90                 :        1085 :     return fdw;
      91                 :             : }
      92                 :             : 
      93                 :             : 
      94                 :             : /*
      95                 :             :  * GetForeignDataWrapperByName - look up the foreign-data wrapper
      96                 :             :  * definition by name.
      97                 :             :  */
      98                 :             : ForeignDataWrapper *
      99                 :         321 : GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
     100                 :             : {
     101                 :         321 :     Oid         fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
     102                 :             : 
     103         [ +  + ]:         317 :     if (!OidIsValid(fdwId))
     104                 :         134 :         return NULL;
     105                 :             : 
     106                 :         183 :     return GetForeignDataWrapper(fdwId);
     107                 :             : }
     108                 :             : 
     109                 :             : 
     110                 :             : /*
     111                 :             :  * GetForeignServer - look up the foreign server definition.
     112                 :             :  */
     113                 :             : ForeignServer *
     114                 :        2901 : GetForeignServer(Oid serverid)
     115                 :             : {
     116                 :        2901 :     return GetForeignServerExtended(serverid, 0);
     117                 :             : }
     118                 :             : 
     119                 :             : 
     120                 :             : /*
     121                 :             :  * GetForeignServerExtended - look up the foreign server definition. If
     122                 :             :  * flags uses FSV_MISSING_OK, return NULL if the object cannot be found
     123                 :             :  * instead of raising an error.
     124                 :             :  */
     125                 :             : ForeignServer *
     126                 :        3047 : GetForeignServerExtended(Oid serverid, uint16 flags)
     127                 :             : {
     128                 :             :     Form_pg_foreign_server serverform;
     129                 :             :     ForeignServer *server;
     130                 :             :     HeapTuple   tp;
     131                 :             :     Datum       datum;
     132                 :             :     bool        isnull;
     133                 :             : 
     134                 :        3047 :     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
     135                 :             : 
     136         [ +  + ]:        3047 :     if (!HeapTupleIsValid(tp))
     137                 :             :     {
     138         [ -  + ]:          13 :         if ((flags & FSV_MISSING_OK) == 0)
     139         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for foreign server %u", serverid);
     140                 :          13 :         return NULL;
     141                 :             :     }
     142                 :             : 
     143                 :        3034 :     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
     144                 :             : 
     145                 :        3034 :     server = palloc_object(ForeignServer);
     146                 :        3034 :     server->serverid = serverid;
     147                 :        3034 :     server->servername = pstrdup(NameStr(serverform->srvname));
     148                 :        3034 :     server->owner = serverform->srvowner;
     149                 :        3034 :     server->fdwid = serverform->srvfdw;
     150                 :             : 
     151                 :             :     /* Extract server type */
     152                 :        3034 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     153                 :             :                             tp,
     154                 :             :                             Anum_pg_foreign_server_srvtype,
     155                 :             :                             &isnull);
     156         [ +  + ]:        3034 :     server->servertype = isnull ? NULL : TextDatumGetCString(datum);
     157                 :             : 
     158                 :             :     /* Extract server version */
     159                 :        3034 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     160                 :             :                             tp,
     161                 :             :                             Anum_pg_foreign_server_srvversion,
     162                 :             :                             &isnull);
     163         [ +  + ]:        3034 :     server->serverversion = isnull ? NULL : TextDatumGetCString(datum);
     164                 :             : 
     165                 :             :     /* Extract the srvoptions */
     166                 :        3034 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     167                 :             :                             tp,
     168                 :             :                             Anum_pg_foreign_server_srvoptions,
     169                 :             :                             &isnull);
     170         [ +  + ]:        3034 :     if (isnull)
     171                 :         678 :         server->options = NIL;
     172                 :             :     else
     173                 :        2356 :         server->options = untransformRelOptions(datum);
     174                 :             : 
     175                 :        3034 :     ReleaseSysCache(tp);
     176                 :             : 
     177                 :        3034 :     return server;
     178                 :             : }
     179                 :             : 
     180                 :             : 
     181                 :             : /*
     182                 :             :  * GetForeignServerByName - look up the foreign server definition by name.
     183                 :             :  */
     184                 :             : ForeignServer *
     185                 :         660 : GetForeignServerByName(const char *srvname, bool missing_ok)
     186                 :             : {
     187                 :         660 :     Oid         serverid = get_foreign_server_oid(srvname, missing_ok);
     188                 :             : 
     189         [ +  + ]:         646 :     if (!OidIsValid(serverid))
     190                 :          28 :         return NULL;
     191                 :             : 
     192                 :         618 :     return GetForeignServer(serverid);
     193                 :             : }
     194                 :             : 
     195                 :             : 
     196                 :             : /*
     197                 :             :  * Retrieve connection string from server's FDW.
     198                 :             :  *
     199                 :             :  * NB: leaks into CurrentMemoryContext.
     200                 :             :  */
     201                 :             : char *
     202                 :          24 : ForeignServerConnectionString(Oid userid, ForeignServer *server)
     203                 :             : {
     204                 :             :     ForeignDataWrapper *fdw;
     205                 :             :     Datum       connection_datum;
     206                 :             : 
     207                 :          24 :     fdw = GetForeignDataWrapper(server->fdwid);
     208                 :             : 
     209         [ +  + ]:          24 :     if (!OidIsValid(fdw->fdwconnection))
     210         [ +  - ]:           4 :         ereport(ERROR,
     211                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     212                 :             :                  errmsg("foreign data wrapper \"%s\" does not support subscription connections",
     213                 :             :                         fdw->fdwname),
     214                 :             :                  errdetail("Foreign data wrapper must be defined with CONNECTION specified.")));
     215                 :             : 
     216                 :          20 :     connection_datum = OidFunctionCall3(fdw->fdwconnection,
     217                 :             :                                         ObjectIdGetDatum(userid),
     218                 :             :                                         ObjectIdGetDatum(server->serverid),
     219                 :             :                                         PointerGetDatum(NULL));
     220                 :             : 
     221                 :          16 :     return text_to_cstring(DatumGetTextPP(connection_datum));
     222                 :             : }
     223                 :             : 
     224                 :             : 
     225                 :             : /*
     226                 :             :  * GetUserMapping - look up the user mapping.
     227                 :             :  *
     228                 :             :  * If no mapping is found for the supplied user, we also look for
     229                 :             :  * PUBLIC mappings (userid == InvalidOid).
     230                 :             :  */
     231                 :             : UserMapping *
     232                 :        1337 : GetUserMapping(Oid userid, Oid serverid)
     233                 :             : {
     234                 :             :     Datum       datum;
     235                 :             :     HeapTuple   tp;
     236                 :             :     bool        isnull;
     237                 :             :     UserMapping *um;
     238                 :             : 
     239                 :        1337 :     tp = SearchSysCache2(USERMAPPINGUSERSERVER,
     240                 :             :                          ObjectIdGetDatum(userid),
     241                 :             :                          ObjectIdGetDatum(serverid));
     242                 :             : 
     243         [ +  + ]:        1337 :     if (!HeapTupleIsValid(tp))
     244                 :             :     {
     245                 :             :         /* Not found for the specific user -- try PUBLIC */
     246                 :          50 :         tp = SearchSysCache2(USERMAPPINGUSERSERVER,
     247                 :             :                              ObjectIdGetDatum(InvalidOid),
     248                 :             :                              ObjectIdGetDatum(serverid));
     249                 :             :     }
     250                 :             : 
     251         [ +  + ]:        1337 :     if (!HeapTupleIsValid(tp))
     252                 :             :     {
     253                 :          10 :         ForeignServer *server = GetForeignServer(serverid);
     254                 :             : 
     255   [ +  -  +  - ]:          10 :         ereport(ERROR,
     256                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     257                 :             :                  errmsg("user mapping not found for user \"%s\", server \"%s\"",
     258                 :             :                         MappingUserName(userid), server->servername)));
     259                 :             :     }
     260                 :             : 
     261                 :        1327 :     um = palloc_object(UserMapping);
     262                 :        1327 :     um->umid = ((Form_pg_user_mapping) GETSTRUCT(tp))->oid;
     263                 :        1327 :     um->userid = userid;
     264                 :        1327 :     um->serverid = serverid;
     265                 :             : 
     266                 :             :     /* Extract the umoptions */
     267                 :        1327 :     datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
     268                 :             :                             tp,
     269                 :             :                             Anum_pg_user_mapping_umoptions,
     270                 :             :                             &isnull);
     271         [ +  + ]:        1327 :     if (isnull)
     272                 :        1285 :         um->options = NIL;
     273                 :             :     else
     274                 :          42 :         um->options = untransformRelOptions(datum);
     275                 :             : 
     276                 :        1327 :     ReleaseSysCache(tp);
     277                 :             : 
     278                 :        1327 :     return um;
     279                 :             : }
     280                 :             : 
     281                 :             : 
     282                 :             : /*
     283                 :             :  * GetForeignTable - look up the foreign table definition by relation oid.
     284                 :             :  */
     285                 :             : ForeignTable *
     286                 :        6682 : GetForeignTable(Oid relid)
     287                 :             : {
     288                 :             :     Form_pg_foreign_table tableform;
     289                 :             :     ForeignTable *ft;
     290                 :             :     HeapTuple   tp;
     291                 :             :     Datum       datum;
     292                 :             :     bool        isnull;
     293                 :             : 
     294                 :        6682 :     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
     295         [ -  + ]:        6682 :     if (!HeapTupleIsValid(tp))
     296         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for foreign table %u", relid);
     297                 :        6682 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
     298                 :             : 
     299                 :        6682 :     ft = palloc_object(ForeignTable);
     300                 :        6682 :     ft->relid = relid;
     301                 :        6682 :     ft->serverid = tableform->ftserver;
     302                 :             : 
     303                 :             :     /* Extract the ftoptions */
     304                 :        6682 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
     305                 :             :                             tp,
     306                 :             :                             Anum_pg_foreign_table_ftoptions,
     307                 :             :                             &isnull);
     308         [ -  + ]:        6682 :     if (isnull)
     309                 :           0 :         ft->options = NIL;
     310                 :             :     else
     311                 :        6682 :         ft->options = untransformRelOptions(datum);
     312                 :             : 
     313                 :        6682 :     ReleaseSysCache(tp);
     314                 :             : 
     315                 :        6682 :     return ft;
     316                 :             : }
     317                 :             : 
     318                 :             : 
     319                 :             : /*
     320                 :             :  * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
     321                 :             :  * as list of DefElem.
     322                 :             :  */
     323                 :             : List *
     324                 :       15565 : GetForeignColumnOptions(Oid relid, AttrNumber attnum)
     325                 :             : {
     326                 :             :     List       *options;
     327                 :             :     HeapTuple   tp;
     328                 :             :     Datum       datum;
     329                 :             :     bool        isnull;
     330                 :             : 
     331                 :       15565 :     tp = SearchSysCache2(ATTNUM,
     332                 :             :                          ObjectIdGetDatum(relid),
     333                 :             :                          Int16GetDatum(attnum));
     334         [ -  + ]:       15565 :     if (!HeapTupleIsValid(tp))
     335         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     336                 :             :              attnum, relid);
     337                 :       15565 :     datum = SysCacheGetAttr(ATTNUM,
     338                 :             :                             tp,
     339                 :             :                             Anum_pg_attribute_attfdwoptions,
     340                 :             :                             &isnull);
     341         [ +  + ]:       15565 :     if (isnull)
     342                 :       12067 :         options = NIL;
     343                 :             :     else
     344                 :        3498 :         options = untransformRelOptions(datum);
     345                 :             : 
     346                 :       15565 :     ReleaseSysCache(tp);
     347                 :             : 
     348                 :       15565 :     return options;
     349                 :             : }
     350                 :             : 
     351                 :             : 
     352                 :             : /*
     353                 :             :  * GetFdwRoutine - call the specified foreign-data wrapper handler routine
     354                 :             :  * to get its FdwRoutine struct.
     355                 :             :  */
     356                 :             : FdwRoutine *
     357                 :         741 : GetFdwRoutine(Oid fdwhandler)
     358                 :             : {
     359                 :             :     Datum       datum;
     360                 :             :     FdwRoutine *routine;
     361                 :             : 
     362                 :             :     /* Check if the access to foreign tables is restricted */
     363         [ +  + ]:         741 :     if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_FOREIGN_TABLE) != 0))
     364                 :             :     {
     365                 :             :         /* there must not be built-in FDW handler  */
     366         [ +  - ]:           1 :         ereport(ERROR,
     367                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     368                 :             :                  errmsg("access to non-system foreign table is restricted")));
     369                 :             :     }
     370                 :             : 
     371                 :         740 :     datum = OidFunctionCall0(fdwhandler);
     372                 :         740 :     routine = (FdwRoutine *) DatumGetPointer(datum);
     373                 :             : 
     374   [ +  -  -  + ]:         740 :     if (routine == NULL || !IsA(routine, FdwRoutine))
     375         [ #  # ]:           0 :         elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
     376                 :             :              fdwhandler);
     377                 :             : 
     378                 :         740 :     return routine;
     379                 :             : }
     380                 :             : 
     381                 :             : 
     382                 :             : /*
     383                 :             :  * GetForeignServerIdByRelId - look up the foreign server
     384                 :             :  * for the given foreign table, and return its OID.
     385                 :             :  */
     386                 :             : Oid
     387                 :        1741 : GetForeignServerIdByRelId(Oid relid)
     388                 :             : {
     389                 :             :     HeapTuple   tp;
     390                 :             :     Form_pg_foreign_table tableform;
     391                 :             :     Oid         serverid;
     392                 :             : 
     393                 :        1741 :     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
     394         [ -  + ]:        1741 :     if (!HeapTupleIsValid(tp))
     395         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for foreign table %u", relid);
     396                 :        1741 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
     397                 :        1741 :     serverid = tableform->ftserver;
     398                 :        1741 :     ReleaseSysCache(tp);
     399                 :             : 
     400                 :        1741 :     return serverid;
     401                 :             : }
     402                 :             : 
     403                 :             : 
     404                 :             : /*
     405                 :             :  * GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper
     406                 :             :  * for the given foreign server, and retrieve its FdwRoutine struct.
     407                 :             :  */
     408                 :             : FdwRoutine *
     409                 :         740 : GetFdwRoutineByServerId(Oid serverid)
     410                 :             : {
     411                 :             :     HeapTuple   tp;
     412                 :             :     Form_pg_foreign_data_wrapper fdwform;
     413                 :             :     Form_pg_foreign_server serverform;
     414                 :             :     Oid         fdwid;
     415                 :             :     Oid         fdwhandler;
     416                 :             : 
     417                 :             :     /* Get foreign-data wrapper OID for the server. */
     418                 :         740 :     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
     419         [ -  + ]:         740 :     if (!HeapTupleIsValid(tp))
     420         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for foreign server %u", serverid);
     421                 :         740 :     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
     422                 :         740 :     fdwid = serverform->srvfdw;
     423                 :         740 :     ReleaseSysCache(tp);
     424                 :             : 
     425                 :             :     /* Get handler function OID for the FDW. */
     426                 :         740 :     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
     427         [ -  + ]:         740 :     if (!HeapTupleIsValid(tp))
     428         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
     429                 :         740 :     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
     430                 :         740 :     fdwhandler = fdwform->fdwhandler;
     431                 :             : 
     432                 :             :     /* Complain if FDW has been set to NO HANDLER. */
     433         [ +  + ]:         740 :     if (!OidIsValid(fdwhandler))
     434         [ +  - ]:           9 :         ereport(ERROR,
     435                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     436                 :             :                  errmsg("foreign-data wrapper \"%s\" has no handler",
     437                 :             :                         NameStr(fdwform->fdwname))));
     438                 :             : 
     439                 :         731 :     ReleaseSysCache(tp);
     440                 :             : 
     441                 :             :     /* And finally, call the handler function. */
     442                 :         731 :     return GetFdwRoutine(fdwhandler);
     443                 :             : }
     444                 :             : 
     445                 :             : 
     446                 :             : /*
     447                 :             :  * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
     448                 :             :  * for the given foreign table, and retrieve its FdwRoutine struct.
     449                 :             :  */
     450                 :             : FdwRoutine *
     451                 :         408 : GetFdwRoutineByRelId(Oid relid)
     452                 :             : {
     453                 :             :     Oid         serverid;
     454                 :             : 
     455                 :             :     /* Get server OID for the foreign table. */
     456                 :         408 :     serverid = GetForeignServerIdByRelId(relid);
     457                 :             : 
     458                 :             :     /* Now retrieve server's FdwRoutine struct. */
     459                 :         408 :     return GetFdwRoutineByServerId(serverid);
     460                 :             : }
     461                 :             : 
     462                 :             : /*
     463                 :             :  * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper
     464                 :             :  * for the given foreign table, and retrieve its FdwRoutine struct.
     465                 :             :  *
     466                 :             :  * This function is preferred over GetFdwRoutineByRelId because it caches
     467                 :             :  * the data in the relcache entry, saving a number of catalog lookups.
     468                 :             :  *
     469                 :             :  * If makecopy is true then the returned data is freshly palloc'd in the
     470                 :             :  * caller's memory context.  Otherwise, it's a pointer to the relcache data,
     471                 :             :  * which will be lost in any relcache reset --- so don't rely on it long.
     472                 :             :  */
     473                 :             : FdwRoutine *
     474                 :        2699 : GetFdwRoutineForRelation(Relation relation, bool makecopy)
     475                 :             : {
     476                 :             :     FdwRoutine *fdwroutine;
     477                 :             :     FdwRoutine *cfdwroutine;
     478                 :             : 
     479         [ +  + ]:        2699 :     if (relation->rd_fdwroutine == NULL)
     480                 :             :     {
     481                 :             :         /* Get the info by consulting the catalogs and the FDW code */
     482                 :         205 :         fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
     483                 :             : 
     484                 :             :         /* Save the data for later reuse in CacheMemoryContext */
     485                 :         196 :         cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
     486                 :             :                                                         sizeof(FdwRoutine));
     487                 :         196 :         memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
     488                 :         196 :         relation->rd_fdwroutine = cfdwroutine;
     489                 :             : 
     490                 :             :         /* Give back the locally palloc'd copy regardless of makecopy */
     491                 :         196 :         return fdwroutine;
     492                 :             :     }
     493                 :             : 
     494                 :             :     /* We have valid cached data --- does the caller want a copy? */
     495         [ +  + ]:        2494 :     if (makecopy)
     496                 :             :     {
     497                 :        2282 :         fdwroutine = palloc_object(FdwRoutine);
     498                 :        2282 :         memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
     499                 :        2282 :         return fdwroutine;
     500                 :             :     }
     501                 :             : 
     502                 :             :     /* Only a short-lived reference is needed, so just hand back cached copy */
     503                 :         212 :     return relation->rd_fdwroutine;
     504                 :             : }
     505                 :             : 
     506                 :             : 
     507                 :             : /*
     508                 :             :  * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA
     509                 :             :  *
     510                 :             :  * Returns true if given table name should be imported according to the
     511                 :             :  * statement's import filter options.
     512                 :             :  */
     513                 :             : bool
     514                 :          32 : IsImportableForeignTable(const char *tablename,
     515                 :             :                          ImportForeignSchemaStmt *stmt)
     516                 :             : {
     517                 :             :     ListCell   *lc;
     518                 :             : 
     519   [ +  +  +  - ]:          32 :     switch (stmt->list_type)
     520                 :             :     {
     521                 :          22 :         case FDW_IMPORT_SCHEMA_ALL:
     522                 :          22 :             return true;
     523                 :             : 
     524                 :           5 :         case FDW_IMPORT_SCHEMA_LIMIT_TO:
     525   [ +  -  +  -  :           7 :             foreach(lc, stmt->table_list)
                   +  - ]
     526                 :             :             {
     527                 :           7 :                 RangeVar   *rv = (RangeVar *) lfirst(lc);
     528                 :             : 
     529         [ +  + ]:           7 :                 if (strcmp(tablename, rv->relname) == 0)
     530                 :           5 :                     return true;
     531                 :             :             }
     532                 :           0 :             return false;
     533                 :             : 
     534                 :           5 :         case FDW_IMPORT_SCHEMA_EXCEPT:
     535   [ +  -  +  +  :          25 :             foreach(lc, stmt->table_list)
                   +  + ]
     536                 :             :             {
     537                 :          20 :                 RangeVar   *rv = (RangeVar *) lfirst(lc);
     538                 :             : 
     539         [ -  + ]:          20 :                 if (strcmp(tablename, rv->relname) == 0)
     540                 :           0 :                     return false;
     541                 :             :             }
     542                 :           5 :             return true;
     543                 :             :     }
     544                 :           0 :     return false;               /* shouldn't get here */
     545                 :             : }
     546                 :             : 
     547                 :             : 
     548                 :             : /*
     549                 :             :  * pg_options_to_table - Convert options array to name/value table
     550                 :             :  *
     551                 :             :  * This is useful to provide details for information_schema and pg_dump.
     552                 :             :  */
     553                 :             : Datum
     554                 :         612 : pg_options_to_table(PG_FUNCTION_ARGS)
     555                 :             : {
     556                 :         612 :     Datum       array = PG_GETARG_DATUM(0);
     557                 :             :     ListCell   *cell;
     558                 :             :     List       *options;
     559                 :             :     ReturnSetInfo *rsinfo;
     560                 :             : 
     561                 :         612 :     options = untransformRelOptions(array);
     562                 :         612 :     rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     563                 :             : 
     564                 :             :     /* prepare the result set */
     565                 :         612 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     566                 :             : 
     567   [ +  -  +  +  :        1718 :     foreach(cell, options)
                   +  + ]
     568                 :             :     {
     569                 :        1106 :         DefElem    *def = lfirst(cell);
     570                 :             :         Datum       values[2];
     571                 :             :         bool        nulls[2];
     572                 :             : 
     573                 :        1106 :         values[0] = CStringGetTextDatum(def->defname);
     574                 :        1106 :         nulls[0] = false;
     575         [ +  - ]:        1106 :         if (def->arg)
     576                 :             :         {
     577                 :        1106 :             values[1] = CStringGetTextDatum(strVal(def->arg));
     578                 :        1106 :             nulls[1] = false;
     579                 :             :         }
     580                 :             :         else
     581                 :             :         {
     582                 :           0 :             values[1] = (Datum) 0;
     583                 :           0 :             nulls[1] = true;
     584                 :             :         }
     585                 :        1106 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     586                 :             :                              values, nulls);
     587                 :             :     }
     588                 :             : 
     589                 :         612 :     return (Datum) 0;
     590                 :             : }
     591                 :             : 
     592                 :             : 
     593                 :             : /*
     594                 :             :  * Describes the valid options for postgresql FDW, server, and user mapping.
     595                 :             :  */
     596                 :             : struct ConnectionOption
     597                 :             : {
     598                 :             :     const char *optname;
     599                 :             :     Oid         optcontext;     /* Oid of catalog in which option may appear */
     600                 :             : };
     601                 :             : 
     602                 :             : /*
     603                 :             :  * Copied from fe-connect.c PQconninfoOptions.
     604                 :             :  *
     605                 :             :  * The list is small - don't bother with bsearch if it stays so.
     606                 :             :  */
     607                 :             : static const struct ConnectionOption libpq_conninfo_options[] = {
     608                 :             :     {"authtype", ForeignServerRelationId},
     609                 :             :     {"service", ForeignServerRelationId},
     610                 :             :     {"user", UserMappingRelationId},
     611                 :             :     {"password", UserMappingRelationId},
     612                 :             :     {"connect_timeout", ForeignServerRelationId},
     613                 :             :     {"dbname", ForeignServerRelationId},
     614                 :             :     {"host", ForeignServerRelationId},
     615                 :             :     {"hostaddr", ForeignServerRelationId},
     616                 :             :     {"port", ForeignServerRelationId},
     617                 :             :     {"tty", ForeignServerRelationId},
     618                 :             :     {"options", ForeignServerRelationId},
     619                 :             :     {"requiressl", ForeignServerRelationId},
     620                 :             :     {"sslmode", ForeignServerRelationId},
     621                 :             :     {"gsslib", ForeignServerRelationId},
     622                 :             :     {"gssdelegation", ForeignServerRelationId},
     623                 :             :     {NULL, InvalidOid}
     624                 :             : };
     625                 :             : 
     626                 :             : 
     627                 :             : /*
     628                 :             :  * Check if the provided option is one of libpq conninfo options.
     629                 :             :  * context is the Oid of the catalog the option came from, or 0 if we
     630                 :             :  * don't care.
     631                 :             :  */
     632                 :             : static bool
     633                 :          72 : is_conninfo_option(const char *option, Oid context)
     634                 :             : {
     635                 :             :     const struct ConnectionOption *opt;
     636                 :             : 
     637         [ +  + ]:         564 :     for (opt = libpq_conninfo_options; opt->optname; opt++)
     638   [ +  +  +  + ]:         544 :         if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
     639                 :          52 :             return true;
     640                 :          20 :     return false;
     641                 :             : }
     642                 :             : 
     643                 :             : 
     644                 :             : /*
     645                 :             :  * Validate the generic option given to SERVER or USER MAPPING.
     646                 :             :  * Raise an ERROR if the option or its value is considered invalid.
     647                 :             :  *
     648                 :             :  * Valid server options are all libpq conninfo options except
     649                 :             :  * user and password -- these may only appear in USER MAPPING options.
     650                 :             :  *
     651                 :             :  * Caution: this function is deprecated, and is now meant only for testing
     652                 :             :  * purposes, because the list of options it knows about doesn't necessarily
     653                 :             :  * square with those known to whichever libpq instance you might be using.
     654                 :             :  * Inquire of libpq itself, instead.
     655                 :             :  */
     656                 :             : Datum
     657                 :          92 : postgresql_fdw_validator(PG_FUNCTION_ARGS)
     658                 :             : {
     659                 :          92 :     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
     660                 :          92 :     Oid         catalog = PG_GETARG_OID(1);
     661                 :             : 
     662                 :             :     ListCell   *cell;
     663                 :             : 
     664   [ +  +  +  +  :         144 :     foreach(cell, options_list)
                   +  + ]
     665                 :             :     {
     666                 :          72 :         DefElem    *def = lfirst(cell);
     667                 :             : 
     668         [ +  + ]:          72 :         if (!is_conninfo_option(def->defname, catalog))
     669                 :             :         {
     670                 :             :             const struct ConnectionOption *opt;
     671                 :             :             const char *closest_match;
     672                 :             :             ClosestMatchState match_state;
     673                 :          20 :             bool        has_valid_options = false;
     674                 :             : 
     675                 :             :             /*
     676                 :             :              * Unknown option specified, complain about it. Provide a hint
     677                 :             :              * with a valid option that looks similar, if there is one.
     678                 :             :              */
     679                 :          20 :             initClosestMatch(&match_state, def->defname, 4);
     680         [ +  + ]:         320 :             for (opt = libpq_conninfo_options; opt->optname; opt++)
     681                 :             :             {
     682         [ +  + ]:         300 :                 if (catalog == opt->optcontext)
     683                 :             :                 {
     684                 :         120 :                     has_valid_options = true;
     685                 :         120 :                     updateClosestMatch(&match_state, opt->optname);
     686                 :             :                 }
     687                 :             :             }
     688                 :             : 
     689                 :          20 :             closest_match = getClosestMatch(&match_state);
     690   [ +  -  +  +  :          20 :             ereport(ERROR,
                   +  + ]
     691                 :             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     692                 :             :                      errmsg("invalid option \"%s\"", def->defname),
     693                 :             :                      has_valid_options ? closest_match ?
     694                 :             :                      errhint("Perhaps you meant the option \"%s\".",
     695                 :             :                              closest_match) : 0 :
     696                 :             :                      errhint("There are no valid options in this context.")));
     697                 :             : 
     698                 :             :             PG_RETURN_BOOL(false);
     699                 :             :         }
     700                 :             :     }
     701                 :             : 
     702                 :          72 :     PG_RETURN_BOOL(true);
     703                 :             : }
     704                 :             : 
     705                 :             : 
     706                 :             : /*
     707                 :             :  * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
     708                 :             :  *
     709                 :             :  * If missing_ok is false, throw an error if name not found.  If true, just
     710                 :             :  * return InvalidOid.
     711                 :             :  */
     712                 :             : Oid
     713                 :         553 : get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
     714                 :             : {
     715                 :             :     Oid         oid;
     716                 :             : 
     717                 :         553 :     oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME,
     718                 :             :                           Anum_pg_foreign_data_wrapper_oid,
     719                 :             :                           CStringGetDatum(fdwname));
     720   [ +  +  +  + ]:         553 :     if (!OidIsValid(oid) && !missing_ok)
     721         [ +  - ]:          16 :         ereport(ERROR,
     722                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     723                 :             :                  errmsg("foreign-data wrapper \"%s\" does not exist",
     724                 :             :                         fdwname)));
     725                 :         537 :     return oid;
     726                 :             : }
     727                 :             : 
     728                 :             : 
     729                 :             : /*
     730                 :             :  * get_foreign_server_oid - given a server name, look up the OID
     731                 :             :  *
     732                 :             :  * If missing_ok is false, throw an error if name not found.  If true, just
     733                 :             :  * return InvalidOid.
     734                 :             :  */
     735                 :             : Oid
     736                 :        1079 : get_foreign_server_oid(const char *servername, bool missing_ok)
     737                 :             : {
     738                 :             :     Oid         oid;
     739                 :             : 
     740                 :        1079 :     oid = GetSysCacheOid1(FOREIGNSERVERNAME, Anum_pg_foreign_server_oid,
     741                 :             :                           CStringGetDatum(servername));
     742   [ +  +  +  + ]:        1079 :     if (!OidIsValid(oid) && !missing_ok)
     743         [ +  - ]:          26 :         ereport(ERROR,
     744                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     745                 :             :                  errmsg("server \"%s\" does not exist", servername)));
     746                 :        1053 :     return oid;
     747                 :             : }
     748                 :             : 
     749                 :             : /*
     750                 :             :  * Get a copy of an existing local path for a given join relation.
     751                 :             :  *
     752                 :             :  * This function is usually helpful to obtain an alternate local path for EPQ
     753                 :             :  * checks.
     754                 :             :  *
     755                 :             :  * Right now, this function only supports unparameterized foreign joins, so we
     756                 :             :  * only search for unparameterized path in the given list of paths. Since we
     757                 :             :  * are searching for a path which can be used to construct an alternative local
     758                 :             :  * plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
     759                 :             :  * paths.
     760                 :             :  *
     761                 :             :  * If the inner or outer subpath of the chosen path is a ForeignScan, we
     762                 :             :  * replace it with its outer subpath.  For this reason, and also because the
     763                 :             :  * planner might free the original path later, the path returned by this
     764                 :             :  * function is a shallow copy of the original.  There's no need to copy
     765                 :             :  * the substructure, so we don't.
     766                 :             :  *
     767                 :             :  * Since the plan created using this path will presumably only be used to
     768                 :             :  * execute EPQ checks, efficiency of the path is not a concern. But since the
     769                 :             :  * path list in RelOptInfo is anyway sorted by total cost we are likely to
     770                 :             :  * choose the most efficient path, which is all for the best.
     771                 :             :  */
     772                 :             : Path *
     773                 :          78 : GetExistingLocalJoinPath(RelOptInfo *joinrel)
     774                 :             : {
     775                 :             :     ListCell   *lc;
     776                 :             : 
     777                 :             :     Assert(IS_JOIN_REL(joinrel));
     778                 :             : 
     779   [ +  -  +  -  :          78 :     foreach(lc, joinrel->pathlist)
                   +  - ]
     780                 :             :     {
     781                 :          78 :         Path       *path = (Path *) lfirst(lc);
     782                 :          78 :         JoinPath   *joinpath = NULL;
     783                 :             : 
     784                 :             :         /* Skip parameterized paths. */
     785         [ -  + ]:          78 :         if (path->param_info != NULL)
     786                 :           0 :             continue;
     787                 :             : 
     788   [ +  +  +  - ]:          78 :         switch (path->pathtype)
     789                 :             :         {
     790                 :          23 :             case T_HashJoin:
     791                 :             :                 {
     792                 :          23 :                     HashPath   *hash_path = makeNode(HashPath);
     793                 :             : 
     794                 :          23 :                     memcpy(hash_path, path, sizeof(HashPath));
     795                 :          23 :                     joinpath = (JoinPath *) hash_path;
     796                 :             :                 }
     797                 :          23 :                 break;
     798                 :             : 
     799                 :          22 :             case T_NestLoop:
     800                 :             :                 {
     801                 :          22 :                     NestPath   *nest_path = makeNode(NestPath);
     802                 :             : 
     803                 :          22 :                     memcpy(nest_path, path, sizeof(NestPath));
     804                 :          22 :                     joinpath = (JoinPath *) nest_path;
     805                 :             :                 }
     806                 :          22 :                 break;
     807                 :             : 
     808                 :          33 :             case T_MergeJoin:
     809                 :             :                 {
     810                 :          33 :                     MergePath  *merge_path = makeNode(MergePath);
     811                 :             : 
     812                 :          33 :                     memcpy(merge_path, path, sizeof(MergePath));
     813                 :          33 :                     joinpath = (JoinPath *) merge_path;
     814                 :             :                 }
     815                 :          33 :                 break;
     816                 :             : 
     817                 :           0 :             default:
     818                 :             : 
     819                 :             :                 /*
     820                 :             :                  * Just skip anything else. We don't know if corresponding
     821                 :             :                  * plan would build the output row from whole-row references
     822                 :             :                  * of base relations and execute the EPQ checks.
     823                 :             :                  */
     824                 :           0 :                 break;
     825                 :             :         }
     826                 :             : 
     827                 :             :         /* This path isn't good for us, check next. */
     828         [ -  + ]:          78 :         if (!joinpath)
     829                 :           0 :             continue;
     830                 :             : 
     831                 :             :         /*
     832                 :             :          * If either inner or outer path is a ForeignPath corresponding to a
     833                 :             :          * pushed down join, replace it with the fdw_outerpath, so that we
     834                 :             :          * maintain path for EPQ checks built entirely of local join
     835                 :             :          * strategies.
     836                 :             :          */
     837         [ +  - ]:          78 :         if (IsA(joinpath->outerjoinpath, ForeignPath))
     838                 :             :         {
     839                 :             :             ForeignPath *foreign_path;
     840                 :             : 
     841                 :          78 :             foreign_path = (ForeignPath *) joinpath->outerjoinpath;
     842   [ +  +  -  + ]:          78 :             if (IS_JOIN_REL(foreign_path->path.parent))
     843                 :             :             {
     844                 :          20 :                 joinpath->outerjoinpath = foreign_path->fdw_outerpath;
     845                 :             : 
     846         [ +  + ]:          20 :                 if (joinpath->path.pathtype == T_MergeJoin)
     847                 :             :                 {
     848                 :          10 :                     MergePath  *merge_path = (MergePath *) joinpath;
     849                 :             : 
     850                 :             :                     /*
     851                 :             :                      * If the new outer path is already well enough ordered
     852                 :             :                      * for the mergejoin, we can skip doing an explicit sort.
     853                 :             :                      */
     854   [ +  +  +  - ]:          14 :                     if (merge_path->outersortkeys &&
     855                 :           4 :                         pathkeys_count_contained_in(merge_path->outersortkeys,
     856                 :           4 :                                                     joinpath->outerjoinpath->pathkeys,
     857                 :             :                                                     &merge_path->outer_presorted_keys))
     858                 :           4 :                         merge_path->outersortkeys = NIL;
     859                 :             :                 }
     860                 :             :             }
     861                 :             :         }
     862                 :             : 
     863         [ +  + ]:          78 :         if (IsA(joinpath->innerjoinpath, ForeignPath))
     864                 :             :         {
     865                 :             :             ForeignPath *foreign_path;
     866                 :             : 
     867                 :          68 :             foreign_path = (ForeignPath *) joinpath->innerjoinpath;
     868   [ +  -  -  + ]:          68 :             if (IS_JOIN_REL(foreign_path->path.parent))
     869                 :             :             {
     870                 :           0 :                 joinpath->innerjoinpath = foreign_path->fdw_outerpath;
     871                 :             : 
     872         [ #  # ]:           0 :                 if (joinpath->path.pathtype == T_MergeJoin)
     873                 :             :                 {
     874                 :           0 :                     MergePath  *merge_path = (MergePath *) joinpath;
     875                 :             : 
     876                 :             :                     /*
     877                 :             :                      * If the new inner path is already well enough ordered
     878                 :             :                      * for the mergejoin, we can skip doing an explicit sort.
     879                 :             :                      */
     880   [ #  #  #  # ]:           0 :                     if (merge_path->innersortkeys &&
     881                 :           0 :                         pathkeys_contained_in(merge_path->innersortkeys,
     882                 :           0 :                                               joinpath->innerjoinpath->pathkeys))
     883                 :           0 :                         merge_path->innersortkeys = NIL;
     884                 :             :                 }
     885                 :             :             }
     886                 :             :         }
     887                 :             : 
     888                 :          78 :         return (Path *) joinpath;
     889                 :             :     }
     890                 :           0 :     return NULL;
     891                 :             : }
        

Generated by: LCOV version 2.0-1