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

Generated by: LCOV version 2.0-1