@ -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 ;
}