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

Generated by: LCOV version 1.14