1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-31 22:04:40 +03:00

Add the possibility to specify an explicit validator function for foreign-data

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.
This commit is contained in:
Peter Eisentraut
2009-02-24 10:06:36 +00:00
parent f73bed308a
commit 7babccb915
28 changed files with 452 additions and 626 deletions

View File

@ -4,7 +4,7 @@
# Makefile for foreign
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/foreign/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
# $PostgreSQL: pgsql/src/backend/foreign/Makefile,v 1.2 2009/02/24 10:06:32 petere Exp $
#
#-------------------------------------------------------------------------
@ -15,11 +15,3 @@ include $(top_builddir)/src/Makefile.global
OBJS= foreign.o
include $(top_srcdir)/src/backend/common.mk
FDW = dummy postgresql
$(addsuffix -fdw,all install installdirs uninstall distprep):
for dir in $(FDW); do $(MAKE) -C $$dir `echo $@ | sed 's/-fdw$$//'` || exit; done
clean distclean maintainer-clean:
for dir in $(FDW); do $(MAKE) -C $$dir $@ || exit; done

View File

@ -1,27 +0,0 @@
#-------------------------------------------------------------------------
#
# Makefile--
# Makefile for dummy foreign-data wrapper
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/foreign/dummy/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
#
#-------------------------------------------------------------------------
subdir = src/backend/foreign/dummy
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
NAME = dummy_fdw
OBJS = dummy_fdw.o
include $(top_srcdir)/src/Makefile.shlib
all: all-shared-lib
install: all install-lib
installdirs: installdirs-lib
clean distclean maintainer-clean: clean-lib
rm -f $(OBJS)

View File

@ -1,24 +0,0 @@
/*-------------------------------------------------------------------------
*
* dummy_fdw.c
* "dummy" foreign-data wrapper
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/foreign/dummy/dummy_fdw.c,v 1.2 2009/01/01 17:23:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "foreign/foreign.h"
PG_MODULE_MAGIC;
/*
* This looks like a complete waste right now, but it is useful for
* testing, and will become more interesting as more parts of the
* interface are implemented.
*/

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.2 2009/01/01 17:23:42 momjian Exp $
* $PostgreSQL: pgsql/src/backend/foreign/foreign.c,v 1.3 2009/02/24 10:06:32 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,66 +31,12 @@
extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
extern Datum postgresql_fdw_validator(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)
@ -114,15 +60,7 @@ GetForeignDataWrapper(Oid fdwid)
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);
fdw->fdwvalidator = fdwform->fdwvalidator;
/* Extract the options */
datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
@ -387,3 +325,100 @@ pg_options_to_table(PG_FUNCTION_ARGS)
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);
}

View File

@ -1,27 +0,0 @@
#-------------------------------------------------------------------------
#
# Makefile--
# Makefile for postgresql foreign-data wrapper
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/foreign/postgresql/Makefile,v 1.1 2008/12/19 16:25:17 petere Exp $
#
#-------------------------------------------------------------------------
subdir = src/backend/foreign/postgresql
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
NAME = postgresql_fdw
OBJS = postgresql_fdw.o
include $(top_srcdir)/src/Makefile.shlib
all: all-shared-lib
install: all install-lib
installdirs: installdirs-lib
clean distclean maintainer-clean: clean-lib
rm -f $(OBJS)

View File

@ -1,123 +0,0 @@
/*-------------------------------------------------------------------------
*
* postgresql_fdw.c
* foreign-data wrapper for postgresql (libpq) connections.
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/foreign/postgresql/postgresql_fdw.c,v 1.2 2009/01/01 17:23:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "nodes/value.h"
#include "nodes/parsenodes.h"
#include "nodes/makefuncs.h"
#include "foreign/foreign.h"
PG_MODULE_MAGIC;
/*
* Describes the valid options for postgresql FDW, server and user mapping.
*/
typedef struct ConnectionOptions {
const char *optname; /* Option name */
GenericOptionFlags optflags; /* Option usage bitmap */
} ConnectionOptions;
/*
* Copied from fe-connect.c PQconninfoOptions.
*
* The list is small - don't bother with bsearch if it stays so.
*/
static ConnectionOptions libpq_conninfo_options[] = {
{ "authtype", ServerOpt },
{ "service", ServerOpt },
{ "user", UserMappingOpt },
{ "password", UserMappingOpt },
{ "connect_timeout", ServerOpt },
{ "dbname", ServerOpt },
{ "host", ServerOpt },
{ "hostaddr", ServerOpt },
{ "port", ServerOpt },
{ "tty", ServerOpt },
{ "options", ServerOpt },
{ "requiressl", ServerOpt },
{ "sslmode", ServerOpt },
{ "gsslib", ServerOpt },
{ NULL, InvalidOpt }
};
void _PG_fini(void);
/*
* Check if the provided option is one of libpq conninfo options.
* We look at only options with matching flags.
*/
static bool
is_conninfo_option(const char *option, GenericOptionFlags flags)
{
ConnectionOptions *opt;
for (opt = libpq_conninfo_options; opt->optname != NULL; opt++)
if (flags & opt->optflags && 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.
*/
void
_pg_validateOptionList(ForeignDataWrapper *fdw, GenericOptionFlags flags,
List *options)
{
ListCell *cell;
foreach (cell, options)
{
DefElem *def = lfirst(cell);
if (!is_conninfo_option(def->defname, flags))
{
ConnectionOptions *opt;
StringInfoData buf;
const char *objtype;
/*
* 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 != NULL; opt++)
if (flags & opt->optflags)
appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
opt->optname);
if (flags & ServerOpt)
objtype = "server";
else if (flags & UserMappingOpt)
objtype = "user mapping";
else if (flags & FdwOpt)
objtype = "foreign-data wrapper";
else
objtype = "???";
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid option \"%s\" to %s", def->defname, objtype),
errhint("valid %s options are: %s", objtype, buf.data)));
}
}
}