diff options
Diffstat (limited to 'src/backend/foreign/foreign.c')
-rw-r--r-- | src/backend/foreign/foreign.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c new file mode 100644 index 00000000000..2fdb84ac1b4 --- /dev/null +++ b/src/backend/foreign/foreign.c @@ -0,0 +1,389 @@ +/*------------------------------------------------------------------------- + * + * foreign.c + * support for foreign-data wrappers, servers and user mappings. + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.1 2008/12/19 16:25:17 petere Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/reloptions.h" +#include "catalog/namespace.h" +#include "catalog/pg_foreign_data_wrapper.h" +#include "catalog/pg_foreign_server.h" +#include "catalog/pg_type.h" +#include "catalog/pg_user_mapping.h" +#include "foreign/foreign.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" +#include "utils/acl.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/syscache.h" + + +extern Datum pg_options_to_table(PG_FUNCTION_ARGS); + + +/* list of currently loaded foreign-data wrapper interfaces */ +static List *loaded_fdw_interfaces = NIL; + + +/* + * GetForeignDataWrapperLibrary - return the named FDW library. If it + * is already loaded, use that. Otherwise allocate, initialize, and + * store in cache. + */ +ForeignDataWrapperLibrary * +GetForeignDataWrapperLibrary(const char *libname) +{ + MemoryContext oldcontext; + void *libhandle = NULL; + ForeignDataWrapperLibrary *fdwl = NULL; + ListCell *cell; + + /* See if we have the FDW library is already loaded */ + foreach (cell, loaded_fdw_interfaces) + { + fdwl = lfirst(cell); + if (strcmp(fdwl->libname, libname) == 0) + return fdwl; + } + + /* + * We don't have it yet, so load and add. Attempt a load_file() + * first to filter out any missing or unloadable libraries. + */ + load_file(libname, false); + + oldcontext = MemoryContextSwitchTo(TopMemoryContext); + + fdwl = palloc(sizeof(*fdwl)); + fdwl->libname = pstrdup(libname); + loaded_fdw_interfaces = lappend(loaded_fdw_interfaces, fdwl); + + MemoryContextSwitchTo(oldcontext); + + /* + * Now look up the foreign data wrapper functions. + */ +#define LOOKUP_FUNCTION(name) \ + (void *)(libhandle ? \ + lookup_external_function(libhandle, name) \ + : load_external_function(fdwl->libname, name, false, &libhandle)) + + fdwl->validateOptionList = LOOKUP_FUNCTION("_pg_validateOptionList"); + + return fdwl; +} + + +/* + * GetForeignDataWrapper - look up the foreign-data wrapper by OID. + * + * Here we also deal with loading the FDW library and looking up the + * actual functions. + */ +ForeignDataWrapper * +GetForeignDataWrapper(Oid fdwid) +{ + Form_pg_foreign_data_wrapper fdwform; + ForeignDataWrapper *fdw; + Datum datum; + HeapTuple tp; + bool isnull; + + tp = SearchSysCache(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(fdwid), + 0, 0, 0); + + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid); + + fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp); + + fdw = palloc(sizeof(ForeignDataWrapper)); + fdw->fdwid = fdwid; + fdw->owner = fdwform->fdwowner; + fdw->fdwname = pstrdup(NameStr(fdwform->fdwname)); + + /* Extract library name */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_fdwlibrary, + &isnull); + fdw->fdwlibrary = pstrdup(TextDatumGetCString(datum)); + + fdw->lib = GetForeignDataWrapperLibrary(fdw->fdwlibrary); + + /* Extract the options */ + datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, + tp, + Anum_pg_foreign_data_wrapper_fdwoptions, + &isnull); + fdw->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return fdw; +} + + +/* + * GetForeignDataWrapperOidByName - look up the foreign-data wrapper + * OID by name. + */ +Oid +GetForeignDataWrapperOidByName(const char *fdwname, bool missing_ok) +{ + Oid fdwId; + + fdwId = GetSysCacheOid(FOREIGNDATAWRAPPERNAME, + CStringGetDatum(fdwname), + 0, 0, 0); + + if (!OidIsValid(fdwId) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign-data wrapper \"%s\" does not exist", fdwname))); + + return fdwId; +} + + +/* + * GetForeignDataWrapperByName - look up the foreign-data wrapper + * definition by name. + */ +ForeignDataWrapper * +GetForeignDataWrapperByName(const char *fdwname, bool missing_ok) +{ + Oid fdwId = GetForeignDataWrapperOidByName(fdwname, missing_ok); + + if (!OidIsValid(fdwId) && missing_ok) + return NULL; + + return GetForeignDataWrapper(fdwId); +} + + +/* + * GetForeignServer - look up the foreign server definition. + */ +ForeignServer * +GetForeignServer(Oid serverid) +{ + Form_pg_foreign_server serverform; + ForeignServer *server; + HeapTuple tp; + Datum datum; + bool isnull; + + tp = SearchSysCache(FOREIGNSERVEROID, + ObjectIdGetDatum(serverid), + 0, 0, 0); + + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for foreign server %u", serverid); + + serverform = (Form_pg_foreign_server) GETSTRUCT(tp); + + server = palloc(sizeof(ForeignServer)); + server->serverid = serverid; + server->servername = pstrdup(NameStr(serverform->srvname)); + server->owner = serverform->srvowner; + server->fdwid = serverform->srvfdw; + + /* Extract server type */ + datum = SysCacheGetAttr(FOREIGNSERVEROID, + tp, + Anum_pg_foreign_server_srvtype, + &isnull); + server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract server version */ + datum = SysCacheGetAttr(FOREIGNSERVEROID, + tp, + Anum_pg_foreign_server_srvversion, + &isnull); + server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum)); + + /* Extract the srvoptions */ + datum = SysCacheGetAttr(FOREIGNSERVEROID, + tp, + Anum_pg_foreign_server_srvoptions, + &isnull); + + /* untransformRelOptions does exactly what we want - avoid duplication */ + server->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return server; +} + + +/* + * GetForeignServerByName - look up the foreign server oid by name. + */ +Oid +GetForeignServerOidByName(const char *srvname, bool missing_ok) +{ + Oid serverid; + + serverid = GetSysCacheOid(FOREIGNSERVERNAME, + CStringGetDatum(srvname), + 0, 0, 0); + + if (!OidIsValid(serverid) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("server \"%s\" does not exist", srvname))); + + return serverid; +} + + +/* + * GetForeignServerByName - look up the foreign server definition by name. + */ +ForeignServer * +GetForeignServerByName(const char *srvname, bool missing_ok) +{ + Oid serverid = GetForeignServerOidByName(srvname, missing_ok); + + if (!OidIsValid(serverid) && missing_ok) + return NULL; + + return GetForeignServer(serverid); +} + + +/* + * GetUserMapping - look up the user mapping. + * + * If no mapping is found for the supplied user, we also look for + * PUBLIC mappings (userid == InvalidOid). + */ +UserMapping * +GetUserMapping(Oid userid, Oid serverid) +{ + Form_pg_user_mapping umform; + Datum datum; + HeapTuple tp; + bool isnull; + UserMapping *um; + + tp = SearchSysCache(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(userid), + ObjectIdGetDatum(serverid), + 0, 0); + + if (!HeapTupleIsValid(tp)) + { + /* Not found for the specific user -- try PUBLIC */ + tp = SearchSysCache(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(serverid), + 0, 0); + } + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("user mapping not found for \"%s\"", + MappingUserName(userid)))); + + umform = (Form_pg_user_mapping) GETSTRUCT(tp); + + /* Extract the umoptions */ + datum = SysCacheGetAttr(USERMAPPINGUSERSERVER, + tp, + Anum_pg_user_mapping_umoptions, + &isnull); + + um = palloc(sizeof(UserMapping)); + um->userid = userid; + um->serverid = serverid; + um->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return um; +} + + +/* + * deflist_to_tuplestore - Helper function to convert DefElem list to + * tuplestore usable in SRF. + */ +static void +deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options) +{ + ListCell *cell; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + Datum values[2]; + bool nulls[2] = { 0 }; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not allowed in this context"))); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* + * Now prepare the result set. + */ + tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc); + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + foreach (cell, options) + { + DefElem *def = lfirst(cell); + + values[0] = CStringGetTextDatum(def->defname); + values[1] = CStringGetTextDatum(((Value *)def->arg)->val.str); + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + MemoryContextSwitchTo(oldcontext); +} + + +/* + * Convert options array to name/value table. Useful for information + * schema and pg_dump. + */ +Datum +pg_options_to_table(PG_FUNCTION_ARGS) +{ + Datum array = PG_GETARG_DATUM(0); + + deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, untransformRelOptions(array)); + + return (Datum) 0; +} |