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

Generated by: LCOV version 1.13