LCOV - code coverage report
Current view: top level - src/backend/foreign - foreign.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.0 % 261 235
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 22 22
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/tuplestore.h"
      32              : #include "utils/varlena.h"
      33              : 
      34              : 
      35              : /*
      36              :  * GetForeignDataWrapper -  look up the foreign-data wrapper by OID.
      37              :  */
      38              : ForeignDataWrapper *
      39          971 : GetForeignDataWrapper(Oid fdwid)
      40              : {
      41          971 :     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         1071 : 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         1071 :     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
      60              : 
      61         1071 :     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         1059 :     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
      69              : 
      70         1059 :     fdw = palloc_object(ForeignDataWrapper);
      71         1059 :     fdw->fdwid = fdwid;
      72         1059 :     fdw->owner = fdwform->fdwowner;
      73         1059 :     fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
      74         1059 :     fdw->fdwhandler = fdwform->fdwhandler;
      75         1059 :     fdw->fdwvalidator = fdwform->fdwvalidator;
      76         1059 :     fdw->fdwconnection = fdwform->fdwconnection;
      77              : 
      78              :     /* Extract the fdwoptions */
      79         1059 :     datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
      80              :                             tp,
      81              :                             Anum_pg_foreign_data_wrapper_fdwoptions,
      82              :                             &isnull);
      83         1059 :     if (isnull)
      84          891 :         fdw->options = NIL;
      85              :     else
      86          168 :         fdw->options = untransformRelOptions(datum);
      87              : 
      88         1059 :     ReleaseSysCache(tp);
      89              : 
      90         1059 :     return fdw;
      91              : }
      92              : 
      93              : 
      94              : /*
      95              :  * GetForeignDataWrapperByName - look up the foreign-data wrapper
      96              :  * definition by name.
      97              :  */
      98              : ForeignDataWrapper *
      99          298 : GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
     100              : {
     101          298 :     Oid         fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
     102              : 
     103          294 :     if (!OidIsValid(fdwId))
     104          116 :         return NULL;
     105              : 
     106          178 :     return GetForeignDataWrapper(fdwId);
     107              : }
     108              : 
     109              : 
     110              : /*
     111              :  * GetForeignServer - look up the foreign server definition.
     112              :  */
     113              : ForeignServer *
     114         2812 : GetForeignServer(Oid serverid)
     115              : {
     116         2812 :     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         2957 : 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         2957 :     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
     135              : 
     136         2957 :     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         2944 :     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
     144              : 
     145         2944 :     server = palloc_object(ForeignServer);
     146         2944 :     server->serverid = serverid;
     147         2944 :     server->servername = pstrdup(NameStr(serverform->srvname));
     148         2944 :     server->owner = serverform->srvowner;
     149         2944 :     server->fdwid = serverform->srvfdw;
     150              : 
     151              :     /* Extract server type */
     152         2944 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     153              :                             tp,
     154              :                             Anum_pg_foreign_server_srvtype,
     155              :                             &isnull);
     156         2944 :     server->servertype = isnull ? NULL : TextDatumGetCString(datum);
     157              : 
     158              :     /* Extract server version */
     159         2944 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     160              :                             tp,
     161              :                             Anum_pg_foreign_server_srvversion,
     162              :                             &isnull);
     163         2944 :     server->serverversion = isnull ? NULL : TextDatumGetCString(datum);
     164              : 
     165              :     /* Extract the srvoptions */
     166         2944 :     datum = SysCacheGetAttr(FOREIGNSERVEROID,
     167              :                             tp,
     168              :                             Anum_pg_foreign_server_srvoptions,
     169              :                             &isnull);
     170         2944 :     if (isnull)
     171          675 :         server->options = NIL;
     172              :     else
     173         2269 :         server->options = untransformRelOptions(datum);
     174              : 
     175         2944 :     ReleaseSysCache(tp);
     176              : 
     177         2944 :     return server;
     178              : }
     179              : 
     180              : 
     181              : /*
     182              :  * GetForeignServerByName - look up the foreign server definition by name.
     183              :  */
     184              : ForeignServer *
     185          644 : GetForeignServerByName(const char *srvname, bool missing_ok)
     186              : {
     187          644 :     Oid         serverid = get_foreign_server_oid(srvname, missing_ok);
     188              : 
     189          630 :     if (!OidIsValid(serverid))
     190           28 :         return NULL;
     191              : 
     192          602 :     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           22 : ForeignServerConnectionString(Oid userid, ForeignServer *server)
     203              : {
     204              :     ForeignDataWrapper *fdw;
     205              :     Datum       connection_datum;
     206              : 
     207           22 :     fdw = GetForeignDataWrapper(server->fdwid);
     208              : 
     209           22 :     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           18 :     connection_datum = OidFunctionCall3(fdw->fdwconnection,
     217              :                                         ObjectIdGetDatum(userid),
     218              :                                         ObjectIdGetDatum(server->serverid),
     219              :                                         PointerGetDatum(NULL));
     220              : 
     221           18 :     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         1292 : GetUserMapping(Oid userid, Oid serverid)
     233              : {
     234              :     Datum       datum;
     235              :     HeapTuple   tp;
     236              :     bool        isnull;
     237              :     UserMapping *um;
     238              : 
     239         1292 :     tp = SearchSysCache2(USERMAPPINGUSERSERVER,
     240              :                          ObjectIdGetDatum(userid),
     241              :                          ObjectIdGetDatum(serverid));
     242              : 
     243         1292 :     if (!HeapTupleIsValid(tp))
     244              :     {
     245              :         /* Not found for the specific user -- try PUBLIC */
     246           48 :         tp = SearchSysCache2(USERMAPPINGUSERSERVER,
     247              :                              ObjectIdGetDatum(InvalidOid),
     248              :                              ObjectIdGetDatum(serverid));
     249              :     }
     250              : 
     251         1292 :     if (!HeapTupleIsValid(tp))
     252              :     {
     253            6 :         ForeignServer *server = GetForeignServer(serverid);
     254              : 
     255            6 :         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         1286 :     um = palloc_object(UserMapping);
     262         1286 :     um->umid = ((Form_pg_user_mapping) GETSTRUCT(tp))->oid;
     263         1286 :     um->userid = userid;
     264         1286 :     um->serverid = serverid;
     265              : 
     266              :     /* Extract the umoptions */
     267         1286 :     datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
     268              :                             tp,
     269              :                             Anum_pg_user_mapping_umoptions,
     270              :                             &isnull);
     271         1286 :     if (isnull)
     272         1256 :         um->options = NIL;
     273              :     else
     274           30 :         um->options = untransformRelOptions(datum);
     275              : 
     276         1286 :     ReleaseSysCache(tp);
     277              : 
     278         1286 :     return um;
     279              : }
     280              : 
     281              : 
     282              : /*
     283              :  * GetForeignTable - look up the foreign table definition by relation oid.
     284              :  */
     285              : ForeignTable *
     286         6555 : GetForeignTable(Oid relid)
     287              : {
     288              :     Form_pg_foreign_table tableform;
     289              :     ForeignTable *ft;
     290              :     HeapTuple   tp;
     291              :     Datum       datum;
     292              :     bool        isnull;
     293              : 
     294         6555 :     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
     295         6555 :     if (!HeapTupleIsValid(tp))
     296            0 :         elog(ERROR, "cache lookup failed for foreign table %u", relid);
     297         6555 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
     298              : 
     299         6555 :     ft = palloc_object(ForeignTable);
     300         6555 :     ft->relid = relid;
     301         6555 :     ft->serverid = tableform->ftserver;
     302              : 
     303              :     /* Extract the ftoptions */
     304         6555 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
     305              :                             tp,
     306              :                             Anum_pg_foreign_table_ftoptions,
     307              :                             &isnull);
     308         6555 :     if (isnull)
     309            0 :         ft->options = NIL;
     310              :     else
     311         6555 :         ft->options = untransformRelOptions(datum);
     312              : 
     313         6555 :     ReleaseSysCache(tp);
     314              : 
     315         6555 :     return ft;
     316              : }
     317              : 
     318              : 
     319              : /*
     320              :  * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
     321              :  * as list of DefElem.
     322              :  */
     323              : List *
     324        15464 : GetForeignColumnOptions(Oid relid, AttrNumber attnum)
     325              : {
     326              :     List       *options;
     327              :     HeapTuple   tp;
     328              :     Datum       datum;
     329              :     bool        isnull;
     330              : 
     331        15464 :     tp = SearchSysCache2(ATTNUM,
     332              :                          ObjectIdGetDatum(relid),
     333              :                          Int16GetDatum(attnum));
     334        15464 :     if (!HeapTupleIsValid(tp))
     335            0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
     336              :              attnum, relid);
     337        15464 :     datum = SysCacheGetAttr(ATTNUM,
     338              :                             tp,
     339              :                             Anum_pg_attribute_attfdwoptions,
     340              :                             &isnull);
     341        15464 :     if (isnull)
     342        11979 :         options = NIL;
     343              :     else
     344         3485 :         options = untransformRelOptions(datum);
     345              : 
     346        15464 :     ReleaseSysCache(tp);
     347              : 
     348        15464 :     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          725 : GetFdwRoutine(Oid fdwhandler)
     358              : {
     359              :     Datum       datum;
     360              :     FdwRoutine *routine;
     361              : 
     362              :     /* Check if the access to foreign tables is restricted */
     363          725 :     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          724 :     datum = OidFunctionCall0(fdwhandler);
     372          724 :     routine = (FdwRoutine *) DatumGetPointer(datum);
     373              : 
     374          724 :     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          724 :     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         1712 : GetForeignServerIdByRelId(Oid relid)
     388              : {
     389              :     HeapTuple   tp;
     390              :     Form_pg_foreign_table tableform;
     391              :     Oid         serverid;
     392              : 
     393         1712 :     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
     394         1712 :     if (!HeapTupleIsValid(tp))
     395            0 :         elog(ERROR, "cache lookup failed for foreign table %u", relid);
     396         1712 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
     397         1712 :     serverid = tableform->ftserver;
     398         1712 :     ReleaseSysCache(tp);
     399              : 
     400         1712 :     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          724 : 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          724 :     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
     419          724 :     if (!HeapTupleIsValid(tp))
     420            0 :         elog(ERROR, "cache lookup failed for foreign server %u", serverid);
     421          724 :     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
     422          724 :     fdwid = serverform->srvfdw;
     423          724 :     ReleaseSysCache(tp);
     424              : 
     425              :     /* Get handler function OID for the FDW. */
     426          724 :     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
     427          724 :     if (!HeapTupleIsValid(tp))
     428            0 :         elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
     429          724 :     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
     430          724 :     fdwhandler = fdwform->fdwhandler;
     431              : 
     432              :     /* Complain if FDW has been set to NO HANDLER. */
     433          724 :     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          715 :     ReleaseSysCache(tp);
     440              : 
     441              :     /* And finally, call the handler function. */
     442          715 :     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          395 : GetFdwRoutineByRelId(Oid relid)
     452              : {
     453              :     Oid         serverid;
     454              : 
     455              :     /* Get server OID for the foreign table. */
     456          395 :     serverid = GetForeignServerIdByRelId(relid);
     457              : 
     458              :     /* Now retrieve server's FdwRoutine struct. */
     459          395 :     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         2647 : GetFdwRoutineForRelation(Relation relation, bool makecopy)
     475              : {
     476              :     FdwRoutine *fdwroutine;
     477              :     FdwRoutine *cfdwroutine;
     478              : 
     479         2647 :     if (relation->rd_fdwroutine == NULL)
     480              :     {
     481              :         /* Get the info by consulting the catalogs and the FDW code */
     482          192 :         fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
     483              : 
     484              :         /* Save the data for later reuse in CacheMemoryContext */
     485          183 :         cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
     486              :                                                         sizeof(FdwRoutine));
     487          183 :         memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
     488          183 :         relation->rd_fdwroutine = cfdwroutine;
     489              : 
     490              :         /* Give back the locally palloc'd copy regardless of makecopy */
     491          183 :         return fdwroutine;
     492              :     }
     493              : 
     494              :     /* We have valid cached data --- does the caller want a copy? */
     495         2455 :     if (makecopy)
     496              :     {
     497         2252 :         fdwroutine = palloc_object(FdwRoutine);
     498         2252 :         memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
     499         2252 :         return fdwroutine;
     500              :     }
     501              : 
     502              :     /* Only a short-lived reference is needed, so just hand back cached copy */
     503          203 :     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          607 : pg_options_to_table(PG_FUNCTION_ARGS)
     555              : {
     556          607 :     Datum       array = PG_GETARG_DATUM(0);
     557              :     ListCell   *cell;
     558              :     List       *options;
     559              :     ReturnSetInfo *rsinfo;
     560              : 
     561          607 :     options = untransformRelOptions(array);
     562          607 :     rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     563              : 
     564              :     /* prepare the result set */
     565          607 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     566              : 
     567         1708 :     foreach(cell, options)
     568              :     {
     569         1101 :         DefElem    *def = lfirst(cell);
     570              :         Datum       values[2];
     571              :         bool        nulls[2];
     572              : 
     573         1101 :         values[0] = CStringGetTextDatum(def->defname);
     574         1101 :         nulls[0] = false;
     575         1101 :         if (def->arg)
     576              :         {
     577         1101 :             values[1] = CStringGetTextDatum(strVal(def->arg));
     578         1101 :             nulls[1] = false;
     579              :         }
     580              :         else
     581              :         {
     582            0 :             values[1] = (Datum) 0;
     583            0 :             nulls[1] = true;
     584              :         }
     585         1101 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     586              :                              values, nulls);
     587              :     }
     588              : 
     589          607 :     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          512 : get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
     714              : {
     715              :     Oid         oid;
     716              : 
     717          512 :     oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME,
     718              :                           Anum_pg_foreign_data_wrapper_oid,
     719              :                           CStringGetDatum(fdwname));
     720          512 :     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          496 :     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         1034 : get_foreign_server_oid(const char *servername, bool missing_ok)
     737              : {
     738              :     Oid         oid;
     739              : 
     740         1034 :     oid = GetSysCacheOid1(FOREIGNSERVERNAME, Anum_pg_foreign_server_oid,
     741              :                           CStringGetDatum(servername));
     742         1034 :     if (!OidIsValid(oid) && !missing_ok)
     743           26 :         ereport(ERROR,
     744              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     745              :                  errmsg("server \"%s\" does not exist", servername)));
     746         1008 :     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