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

Generated by: LCOV version 1.16