LCOV - code coverage report
Current view: top level - src/backend/foreign - foreign.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.8 % 254 228
Test Date: 2026-03-03 13:15:30 Functions: 100.0 % 21 21
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1