1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Create an improved FDW option validator function for contrib/dblink.

dblink now has its own validator function dblink_fdw_validator(), which is
better than the core function postgresql_fdw_validator() because it gets
the list of legal options from libpq instead of having a hard-wired list.

Make the dblink extension module provide a standard foreign data wrapper
dblink_fdw that encapsulates use of this validator, and recommend use of
that wrapper instead of making up wrappers on the fly.

Unfortunately, because ad-hoc wrappers *were* recommended practice
previously, it's not clear when we can get rid of postgresql_fdw_validator
without causing upgrade problems.  But this is a step in the right
direction.

Shigeru Hanada, reviewed by KaiGai Kohei
This commit is contained in:
Tom Lane
2012-10-10 16:53:08 -04:00
parent 392b2e5010
commit 8255566f9d
9 changed files with 188 additions and 17 deletions

View File

@ -37,9 +37,12 @@
#include "libpq-fe.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "executor/spi.h"
#include "foreign/foreign.h"
#include "funcapi.h"
@ -113,6 +116,8 @@ static char *escape_param_str(const char *from);
static void validate_pkattnums(Relation rel,
int2vector *pkattnums_arg, int32 pknumatts_arg,
int **pkattnums, int *pknumatts);
static bool is_valid_dblink_option(const PQconninfoOption *options,
const char *option, Oid context);
/* Global */
static remoteConn *pconn = NULL;
@ -1912,6 +1917,75 @@ dblink_get_notify(PG_FUNCTION_ARGS)
return (Datum) 0;
}
/*
* Validate the options given to a dblink foreign server or user mapping.
* Raise an error if any option is invalid.
*
* We just check the names of options here, so semantic errors in options,
* such as invalid numeric format, will be detected at the attempt to connect.
*/
PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
static const PQconninfoOption *options = NULL;
/*
* Get list of valid libpq options.
*
* To avoid unnecessary work, we get the list once and use it throughout
* the lifetime of this backend process. We don't need to care about
* memory context issues, because PQconndefaults allocates with malloc.
*/
if (!options)
{
options = PQconndefaults();
if (!options) /* assume reason for failure is OOM */
ereport(ERROR,
(errcode(ERRCODE_FDW_OUT_OF_MEMORY),
errmsg("out of memory"),
errdetail("could not get libpq's default connection options")));
}
/* Validate each supplied option. */
foreach(cell, options_list)
{
DefElem *def = (DefElem *) lfirst(cell);
if (!is_valid_dblink_option(options, def->defname, context))
{
/*
* Unknown option, or invalid option for the context specified,
* so complain about it. Provide a hint with list of valid
* options for the context.
*/
StringInfoData buf;
const PQconninfoOption *opt;
initStringInfo(&buf);
for (opt = options; opt->keyword; opt++)
{
if (is_valid_dblink_option(options, opt->keyword, context))
appendStringInfo(&buf, "%s%s",
(buf.len > 0) ? ", " : "",
opt->keyword);
}
ereport(ERROR,
(errcode(ERRCODE_FDW_OPTION_NAME_NOT_FOUND),
errmsg("invalid option \"%s\"", def->defname),
errhint("Valid options in this context are: %s",
buf.data)));
}
}
PG_RETURN_VOID();
}
/*************************************************************
* internal functions
*/
@ -2768,3 +2842,59 @@ validate_pkattnums(Relation rel,
errmsg("invalid attribute number %d", pkattnum)));
}
}
/*
* Check if the specified connection option is valid.
*
* We basically allow whatever libpq thinks is an option, with these
* restrictions:
* debug options: disallowed
* "client_encoding": disallowed
* "user": valid only in USER MAPPING options
* secure options (eg password): valid only in USER MAPPING options
* others: valid only in FOREIGN SERVER options
*
* We disallow client_encoding because it would be overridden anyway via
* PQclientEncoding; allowing it to be specified would merely promote
* confusion.
*/
static bool
is_valid_dblink_option(const PQconninfoOption *options, const char *option,
Oid context)
{
const PQconninfoOption *opt;
/* Look up the option in libpq result */
for (opt = options; opt->keyword; opt++)
{
if (strcmp(opt->keyword, option) == 0)
break;
}
if (opt->keyword == NULL)
return false;
/* Disallow debug options (particularly "replication") */
if (strchr(opt->dispchar, 'D'))
return false;
/* Disallow "client_encoding" */
if (strcmp(opt->keyword, "client_encoding") == 0)
return false;
/*
* If the option is "user" or marked secure, it should be specified only
* in USER MAPPING. Others should be specified only in SERVER.
*/
if (strcmp(opt->keyword, "user") == 0 || strchr(opt->dispchar, '*'))
{
if (context != UserMappingRelationId)
return false;
}
else
{
if (context != ForeignServerRelationId)
return false;
}
return true;
}