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

Generated by: LCOV version 1.14