Header And Logo

PostgreSQL
| The world's most advanced open source database.

foreign.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * foreign.c
00004  *        support for foreign-data wrappers, servers and user mappings.
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  *
00008  * IDENTIFICATION
00009  *        src/backend/foreign/foreign.c
00010  *
00011  *-------------------------------------------------------------------------
00012  */
00013 #include "postgres.h"
00014 
00015 #include "access/htup_details.h"
00016 #include "access/reloptions.h"
00017 #include "catalog/pg_foreign_data_wrapper.h"
00018 #include "catalog/pg_foreign_server.h"
00019 #include "catalog/pg_foreign_table.h"
00020 #include "catalog/pg_user_mapping.h"
00021 #include "foreign/fdwapi.h"
00022 #include "foreign/foreign.h"
00023 #include "lib/stringinfo.h"
00024 #include "miscadmin.h"
00025 #include "utils/builtins.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028 #include "utils/syscache.h"
00029 
00030 
00031 extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
00032 extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
00033 
00034 
00035 /*
00036  * GetForeignDataWrapper -  look up the foreign-data wrapper by OID.
00037  */
00038 ForeignDataWrapper *
00039 GetForeignDataWrapper(Oid fdwid)
00040 {
00041     Form_pg_foreign_data_wrapper fdwform;
00042     ForeignDataWrapper *fdw;
00043     Datum       datum;
00044     HeapTuple   tp;
00045     bool        isnull;
00046 
00047     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
00048 
00049     if (!HeapTupleIsValid(tp))
00050         elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
00051 
00052     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
00053 
00054     fdw = (ForeignDataWrapper *) palloc(sizeof(ForeignDataWrapper));
00055     fdw->fdwid = fdwid;
00056     fdw->owner = fdwform->fdwowner;
00057     fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
00058     fdw->fdwhandler = fdwform->fdwhandler;
00059     fdw->fdwvalidator = fdwform->fdwvalidator;
00060 
00061     /* Extract the fdwoptions */
00062     datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
00063                             tp,
00064                             Anum_pg_foreign_data_wrapper_fdwoptions,
00065                             &isnull);
00066     if (isnull)
00067         fdw->options = NIL;
00068     else
00069         fdw->options = untransformRelOptions(datum);
00070 
00071     ReleaseSysCache(tp);
00072 
00073     return fdw;
00074 }
00075 
00076 
00077 /*
00078  * GetForeignDataWrapperByName - look up the foreign-data wrapper
00079  * definition by name.
00080  */
00081 ForeignDataWrapper *
00082 GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
00083 {
00084     Oid         fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
00085 
00086     if (!OidIsValid(fdwId))
00087         return NULL;
00088 
00089     return GetForeignDataWrapper(fdwId);
00090 }
00091 
00092 
00093 /*
00094  * GetForeignServer - look up the foreign server definition.
00095  */
00096 ForeignServer *
00097 GetForeignServer(Oid serverid)
00098 {
00099     Form_pg_foreign_server serverform;
00100     ForeignServer *server;
00101     HeapTuple   tp;
00102     Datum       datum;
00103     bool        isnull;
00104 
00105     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
00106 
00107     if (!HeapTupleIsValid(tp))
00108         elog(ERROR, "cache lookup failed for foreign server %u", serverid);
00109 
00110     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
00111 
00112     server = (ForeignServer *) palloc(sizeof(ForeignServer));
00113     server->serverid = serverid;
00114     server->servername = pstrdup(NameStr(serverform->srvname));
00115     server->owner = serverform->srvowner;
00116     server->fdwid = serverform->srvfdw;
00117 
00118     /* Extract server type */
00119     datum = SysCacheGetAttr(FOREIGNSERVEROID,
00120                             tp,
00121                             Anum_pg_foreign_server_srvtype,
00122                             &isnull);
00123     server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
00124 
00125     /* Extract server version */
00126     datum = SysCacheGetAttr(FOREIGNSERVEROID,
00127                             tp,
00128                             Anum_pg_foreign_server_srvversion,
00129                             &isnull);
00130     server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
00131 
00132     /* Extract the srvoptions */
00133     datum = SysCacheGetAttr(FOREIGNSERVEROID,
00134                             tp,
00135                             Anum_pg_foreign_server_srvoptions,
00136                             &isnull);
00137     if (isnull)
00138         server->options = NIL;
00139     else
00140         server->options = untransformRelOptions(datum);
00141 
00142     ReleaseSysCache(tp);
00143 
00144     return server;
00145 }
00146 
00147 
00148 /*
00149  * GetForeignServerByName - look up the foreign server definition by name.
00150  */
00151 ForeignServer *
00152 GetForeignServerByName(const char *srvname, bool missing_ok)
00153 {
00154     Oid         serverid = get_foreign_server_oid(srvname, missing_ok);
00155 
00156     if (!OidIsValid(serverid))
00157         return NULL;
00158 
00159     return GetForeignServer(serverid);
00160 }
00161 
00162 
00163 /*
00164  * GetUserMapping - look up the user mapping.
00165  *
00166  * If no mapping is found for the supplied user, we also look for
00167  * PUBLIC mappings (userid == InvalidOid).
00168  */
00169 UserMapping *
00170 GetUserMapping(Oid userid, Oid serverid)
00171 {
00172     Datum       datum;
00173     HeapTuple   tp;
00174     bool        isnull;
00175     UserMapping *um;
00176 
00177     tp = SearchSysCache2(USERMAPPINGUSERSERVER,
00178                          ObjectIdGetDatum(userid),
00179                          ObjectIdGetDatum(serverid));
00180 
00181     if (!HeapTupleIsValid(tp))
00182     {
00183         /* Not found for the specific user -- try PUBLIC */
00184         tp = SearchSysCache2(USERMAPPINGUSERSERVER,
00185                              ObjectIdGetDatum(InvalidOid),
00186                              ObjectIdGetDatum(serverid));
00187     }
00188 
00189     if (!HeapTupleIsValid(tp))
00190         ereport(ERROR,
00191                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00192                  errmsg("user mapping not found for \"%s\"",
00193                         MappingUserName(userid))));
00194 
00195     um = (UserMapping *) palloc(sizeof(UserMapping));
00196     um->userid = userid;
00197     um->serverid = serverid;
00198 
00199     /* Extract the umoptions */
00200     datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
00201                             tp,
00202                             Anum_pg_user_mapping_umoptions,
00203                             &isnull);
00204     if (isnull)
00205         um->options = NIL;
00206     else
00207         um->options = untransformRelOptions(datum);
00208 
00209     ReleaseSysCache(tp);
00210 
00211     return um;
00212 }
00213 
00214 
00215 /*
00216  * GetForeignTable - look up the foreign table definition by relation oid.
00217  */
00218 ForeignTable *
00219 GetForeignTable(Oid relid)
00220 {
00221     Form_pg_foreign_table tableform;
00222     ForeignTable *ft;
00223     HeapTuple   tp;
00224     Datum       datum;
00225     bool        isnull;
00226 
00227     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
00228     if (!HeapTupleIsValid(tp))
00229         elog(ERROR, "cache lookup failed for foreign table %u", relid);
00230     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
00231 
00232     ft = (ForeignTable *) palloc(sizeof(ForeignTable));
00233     ft->relid = relid;
00234     ft->serverid = tableform->ftserver;
00235 
00236     /* Extract the ftoptions */
00237     datum = SysCacheGetAttr(FOREIGNTABLEREL,
00238                             tp,
00239                             Anum_pg_foreign_table_ftoptions,
00240                             &isnull);
00241     if (isnull)
00242         ft->options = NIL;
00243     else
00244         ft->options = untransformRelOptions(datum);
00245 
00246     ReleaseSysCache(tp);
00247 
00248     return ft;
00249 }
00250 
00251 
00252 /*
00253  * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
00254  * as list of DefElem.
00255  */
00256 List *
00257 GetForeignColumnOptions(Oid relid, AttrNumber attnum)
00258 {
00259     List       *options;
00260     HeapTuple   tp;
00261     Datum       datum;
00262     bool        isnull;
00263 
00264     tp = SearchSysCache2(ATTNUM,
00265                          ObjectIdGetDatum(relid),
00266                          Int16GetDatum(attnum));
00267     if (!HeapTupleIsValid(tp))
00268         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
00269              attnum, relid);
00270     datum = SysCacheGetAttr(ATTNUM,
00271                             tp,
00272                             Anum_pg_attribute_attfdwoptions,
00273                             &isnull);
00274     if (isnull)
00275         options = NIL;
00276     else
00277         options = untransformRelOptions(datum);
00278 
00279     ReleaseSysCache(tp);
00280 
00281     return options;
00282 }
00283 
00284 
00285 /*
00286  * GetFdwRoutine - call the specified foreign-data wrapper handler routine
00287  * to get its FdwRoutine struct.
00288  */
00289 FdwRoutine *
00290 GetFdwRoutine(Oid fdwhandler)
00291 {
00292     Datum       datum;
00293     FdwRoutine *routine;
00294 
00295     datum = OidFunctionCall0(fdwhandler);
00296     routine = (FdwRoutine *) DatumGetPointer(datum);
00297 
00298     if (routine == NULL || !IsA(routine, FdwRoutine))
00299         elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
00300              fdwhandler);
00301 
00302     return routine;
00303 }
00304 
00305 
00306 /*
00307  * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
00308  * for the given foreign table, and retrieve its FdwRoutine struct.
00309  */
00310 FdwRoutine *
00311 GetFdwRoutineByRelId(Oid relid)
00312 {
00313     HeapTuple   tp;
00314     Form_pg_foreign_data_wrapper fdwform;
00315     Form_pg_foreign_server serverform;
00316     Form_pg_foreign_table tableform;
00317     Oid         serverid;
00318     Oid         fdwid;
00319     Oid         fdwhandler;
00320 
00321     /* Get server OID for the foreign table. */
00322     tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
00323     if (!HeapTupleIsValid(tp))
00324         elog(ERROR, "cache lookup failed for foreign table %u", relid);
00325     tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
00326     serverid = tableform->ftserver;
00327     ReleaseSysCache(tp);
00328 
00329     /* Get foreign-data wrapper OID for the server. */
00330     tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
00331     if (!HeapTupleIsValid(tp))
00332         elog(ERROR, "cache lookup failed for foreign server %u", serverid);
00333     serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
00334     fdwid = serverform->srvfdw;
00335     ReleaseSysCache(tp);
00336 
00337     /* Get handler function OID for the FDW. */
00338     tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
00339     if (!HeapTupleIsValid(tp))
00340         elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
00341     fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
00342     fdwhandler = fdwform->fdwhandler;
00343 
00344     /* Complain if FDW has been set to NO HANDLER. */
00345     if (!OidIsValid(fdwhandler))
00346         ereport(ERROR,
00347                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00348                  errmsg("foreign-data wrapper \"%s\" has no handler",
00349                         NameStr(fdwform->fdwname))));
00350 
00351     ReleaseSysCache(tp);
00352 
00353     /* And finally, call the handler function. */
00354     return GetFdwRoutine(fdwhandler);
00355 }
00356 
00357 /*
00358  * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper
00359  * for the given foreign table, and retrieve its FdwRoutine struct.
00360  *
00361  * This function is preferred over GetFdwRoutineByRelId because it caches
00362  * the data in the relcache entry, saving a number of catalog lookups.
00363  *
00364  * If makecopy is true then the returned data is freshly palloc'd in the
00365  * caller's memory context.  Otherwise, it's a pointer to the relcache data,
00366  * which will be lost in any relcache reset --- so don't rely on it long.
00367  */
00368 FdwRoutine *
00369 GetFdwRoutineForRelation(Relation relation, bool makecopy)
00370 {
00371     FdwRoutine *fdwroutine;
00372     FdwRoutine *cfdwroutine;
00373 
00374     if (relation->rd_fdwroutine == NULL)
00375     {
00376         /* Get the info by consulting the catalogs and the FDW code */
00377         fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
00378 
00379         /* Save the data for later reuse in CacheMemoryContext */
00380         cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
00381                                                         sizeof(FdwRoutine));
00382         memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
00383         relation->rd_fdwroutine = cfdwroutine;
00384 
00385         /* Give back the locally palloc'd copy regardless of makecopy */
00386         return fdwroutine;
00387     }
00388 
00389     /* We have valid cached data --- does the caller want a copy? */
00390     if (makecopy)
00391     {
00392         fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine));
00393         memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
00394         return fdwroutine;
00395     }
00396 
00397     /* Only a short-lived reference is needed, so just hand back cached copy */
00398     return relation->rd_fdwroutine;
00399 }
00400 
00401 
00402 /*
00403  * deflist_to_tuplestore - Helper function to convert DefElem list to
00404  * tuplestore usable in SRF.
00405  */
00406 static void
00407 deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
00408 {
00409     ListCell   *cell;
00410     TupleDesc   tupdesc;
00411     Tuplestorestate *tupstore;
00412     Datum       values[2];
00413     bool        nulls[2];
00414     MemoryContext per_query_ctx;
00415     MemoryContext oldcontext;
00416 
00417     /* check to see if caller supports us returning a tuplestore */
00418     if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
00419         ereport(ERROR,
00420                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00421                  errmsg("set-valued function called in context that cannot accept a set")));
00422     if (!(rsinfo->allowedModes & SFRM_Materialize) ||
00423         rsinfo->expectedDesc == NULL)
00424         ereport(ERROR,
00425                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00426                  errmsg("materialize mode required, but it is not allowed in this context")));
00427 
00428     per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
00429     oldcontext = MemoryContextSwitchTo(per_query_ctx);
00430 
00431     /*
00432      * Now prepare the result set.
00433      */
00434     tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
00435     tupstore = tuplestore_begin_heap(true, false, work_mem);
00436     rsinfo->returnMode = SFRM_Materialize;
00437     rsinfo->setResult = tupstore;
00438     rsinfo->setDesc = tupdesc;
00439 
00440     foreach(cell, options)
00441     {
00442         DefElem    *def = lfirst(cell);
00443 
00444         values[0] = CStringGetTextDatum(def->defname);
00445         nulls[0] = false;
00446         if (def->arg)
00447         {
00448             values[1] = CStringGetTextDatum(((Value *) (def->arg))->val.str);
00449             nulls[1] = false;
00450         }
00451         else
00452         {
00453             values[1] = (Datum) 0;
00454             nulls[1] = true;
00455         }
00456         tuplestore_putvalues(tupstore, tupdesc, values, nulls);
00457     }
00458 
00459     /* clean up and return the tuplestore */
00460     tuplestore_donestoring(tupstore);
00461 
00462     MemoryContextSwitchTo(oldcontext);
00463 }
00464 
00465 
00466 /*
00467  * Convert options array to name/value table.  Useful for information
00468  * schema and pg_dump.
00469  */
00470 Datum
00471 pg_options_to_table(PG_FUNCTION_ARGS)
00472 {
00473     Datum       array = PG_GETARG_DATUM(0);
00474 
00475     deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
00476                           untransformRelOptions(array));
00477 
00478     return (Datum) 0;
00479 }
00480 
00481 
00482 /*
00483  * Describes the valid options for postgresql FDW, server, and user mapping.
00484  */
00485 struct ConnectionOption
00486 {
00487     const char *optname;
00488     Oid         optcontext;     /* Oid of catalog in which option may appear */
00489 };
00490 
00491 /*
00492  * Copied from fe-connect.c PQconninfoOptions.
00493  *
00494  * The list is small - don't bother with bsearch if it stays so.
00495  */
00496 static struct ConnectionOption libpq_conninfo_options[] = {
00497     {"authtype", ForeignServerRelationId},
00498     {"service", ForeignServerRelationId},
00499     {"user", UserMappingRelationId},
00500     {"password", UserMappingRelationId},
00501     {"connect_timeout", ForeignServerRelationId},
00502     {"dbname", ForeignServerRelationId},
00503     {"host", ForeignServerRelationId},
00504     {"hostaddr", ForeignServerRelationId},
00505     {"port", ForeignServerRelationId},
00506     {"tty", ForeignServerRelationId},
00507     {"options", ForeignServerRelationId},
00508     {"requiressl", ForeignServerRelationId},
00509     {"sslmode", ForeignServerRelationId},
00510     {"gsslib", ForeignServerRelationId},
00511     {NULL, InvalidOid}
00512 };
00513 
00514 
00515 /*
00516  * Check if the provided option is one of libpq conninfo options.
00517  * context is the Oid of the catalog the option came from, or 0 if we
00518  * don't care.
00519  */
00520 static bool
00521 is_conninfo_option(const char *option, Oid context)
00522 {
00523     struct ConnectionOption *opt;
00524 
00525     for (opt = libpq_conninfo_options; opt->optname; opt++)
00526         if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
00527             return true;
00528     return false;
00529 }
00530 
00531 
00532 /*
00533  * Validate the generic option given to SERVER or USER MAPPING.
00534  * Raise an ERROR if the option or its value is considered invalid.
00535  *
00536  * Valid server options are all libpq conninfo options except
00537  * user and password -- these may only appear in USER MAPPING options.
00538  *
00539  * Caution: this function is deprecated, and is now meant only for testing
00540  * purposes, because the list of options it knows about doesn't necessarily
00541  * square with those known to whichever libpq instance you might be using.
00542  * Inquire of libpq itself, instead.
00543  */
00544 Datum
00545 postgresql_fdw_validator(PG_FUNCTION_ARGS)
00546 {
00547     List       *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
00548     Oid         catalog = PG_GETARG_OID(1);
00549 
00550     ListCell   *cell;
00551 
00552     foreach(cell, options_list)
00553     {
00554         DefElem    *def = lfirst(cell);
00555 
00556         if (!is_conninfo_option(def->defname, catalog))
00557         {
00558             struct ConnectionOption *opt;
00559             StringInfoData buf;
00560 
00561             /*
00562              * Unknown option specified, complain about it. Provide a hint
00563              * with list of valid options for the object.
00564              */
00565             initStringInfo(&buf);
00566             for (opt = libpq_conninfo_options; opt->optname; opt++)
00567                 if (catalog == opt->optcontext)
00568                     appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
00569                                      opt->optname);
00570 
00571             ereport(ERROR,
00572                     (errcode(ERRCODE_SYNTAX_ERROR),
00573                      errmsg("invalid option \"%s\"", def->defname),
00574                      errhint("Valid options in this context are: %s",
00575                              buf.data)));
00576 
00577             PG_RETURN_BOOL(false);
00578         }
00579     }
00580 
00581     PG_RETURN_BOOL(true);
00582 }
00583 
00584 
00585 /*
00586  * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
00587  *
00588  * If missing_ok is false, throw an error if name not found.  If true, just
00589  * return InvalidOid.
00590  */
00591 Oid
00592 get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
00593 {
00594     Oid         oid;
00595 
00596     oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fdwname));
00597     if (!OidIsValid(oid) && !missing_ok)
00598         ereport(ERROR,
00599                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00600                  errmsg("foreign-data wrapper \"%s\" does not exist",
00601                         fdwname)));
00602     return oid;
00603 }
00604 
00605 
00606 /*
00607  * get_foreign_server_oid - given a FDW name, look up the OID
00608  *
00609  * If missing_ok is false, throw an error if name not found.  If true, just
00610  * return InvalidOid.
00611  */
00612 Oid
00613 get_foreign_server_oid(const char *servername, bool missing_ok)
00614 {
00615     Oid         oid;
00616 
00617     oid = GetSysCacheOid1(FOREIGNSERVERNAME, CStringGetDatum(servername));
00618     if (!OidIsValid(oid) && !missing_ok)
00619         ereport(ERROR,
00620                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00621                  errmsg("server \"%s\" does not exist", servername)));
00622     return oid;
00623 }