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

Generated by: LCOV version 1.11