mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-05 19:56:52 +02:00
7babccb915
wrappers (similar to procedural languages). This way we don't need to retain the nearly empty libraries, and we are more free in how to implement the wrapper API in the future.
425 lines
10 KiB
C
425 lines
10 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* foreign.c
|
|
* support for foreign-data wrappers, servers and user mappings.
|
|
*
|
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.3 2009/02/24 10:06:32 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);
|
|
extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
|
|
|
|
|
|
|
|
/*
|
|
* GetForeignDataWrapper - look up the foreign-data wrapper by OID.
|
|
*/
|
|
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));
|
|
fdw->fdwvalidator = fdwform->fdwvalidator;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Describes the valid options for postgresql FDW, server, and user mapping.
|
|
*/
|
|
struct ConnectionOption {
|
|
const char *optname;
|
|
Oid optcontext; /* Oid of catalog in which option may appear */
|
|
};
|
|
|
|
/*
|
|
* Copied from fe-connect.c PQconninfoOptions.
|
|
*
|
|
* The list is small - don't bother with bsearch if it stays so.
|
|
*/
|
|
static struct ConnectionOption libpq_conninfo_options[] = {
|
|
{ "authtype", ForeignServerRelationId },
|
|
{ "service", ForeignServerRelationId },
|
|
{ "user", UserMappingRelationId },
|
|
{ "password", UserMappingRelationId },
|
|
{ "connect_timeout", ForeignServerRelationId },
|
|
{ "dbname", ForeignServerRelationId },
|
|
{ "host", ForeignServerRelationId },
|
|
{ "hostaddr", ForeignServerRelationId },
|
|
{ "port", ForeignServerRelationId },
|
|
{ "tty", ForeignServerRelationId },
|
|
{ "options", ForeignServerRelationId },
|
|
{ "requiressl", ForeignServerRelationId },
|
|
{ "sslmode", ForeignServerRelationId },
|
|
{ "gsslib", ForeignServerRelationId },
|
|
{ NULL, InvalidOid }
|
|
};
|
|
|
|
|
|
/*
|
|
* Check if the provided option is one of libpq conninfo options.
|
|
* context is the Oid of the catalog the option came from, or 0 if we
|
|
* don't care.
|
|
*/
|
|
static bool
|
|
is_conninfo_option(const char *option, Oid context)
|
|
{
|
|
struct ConnectionOption *opt;
|
|
|
|
for (opt = libpq_conninfo_options; opt->optname; opt++)
|
|
if ((context == opt->optcontext || context == InvalidOid) && strcmp(opt->optname, option) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Validate the generic option given to SERVER or USER MAPPING.
|
|
* Raise an ERROR if the option or its value is considered
|
|
* invalid.
|
|
*
|
|
* Valid server options are all libpq conninfo options except
|
|
* user and password -- these may only appear in USER MAPPING options.
|
|
*/
|
|
Datum
|
|
postgresql_fdw_validator(PG_FUNCTION_ARGS)
|
|
{
|
|
List* options_list = untransformRelOptions(PG_GETARG_DATUM(0));
|
|
Oid catalog = PG_GETARG_OID(1);
|
|
|
|
ListCell *cell;
|
|
|
|
foreach (cell, options_list)
|
|
{
|
|
DefElem *def = lfirst(cell);
|
|
|
|
if (!is_conninfo_option(def->defname, catalog))
|
|
{
|
|
struct ConnectionOption *opt;
|
|
StringInfoData buf;
|
|
|
|
/*
|
|
* Unknown option specified, complain about it. Provide a hint
|
|
* with list of valid options for the object.
|
|
*/
|
|
initStringInfo(&buf);
|
|
for (opt = libpq_conninfo_options; opt->optname; opt++)
|
|
if (catalog == InvalidOid || catalog == opt->optcontext)
|
|
appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
|
|
opt->optname);
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("invalid option \"%s\"", def->defname),
|
|
errhint("Valid options in this context are: %s", buf.data)));
|
|
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
}
|
|
|
|
PG_RETURN_BOOL(true);
|
|
}
|