diff --git a/contrib/dblink/Makefile b/contrib/dblink/Makefile
index ac63748..a27db88 100644
--- a/contrib/dblink/Makefile
+++ b/contrib/dblink/Makefile
@@ -7,7 +7,7 @@ SHLIB_LINK = $(libpq)
SHLIB_PREREQS = submake-libpq
EXTENSION = dblink
-DATA = dblink--1.0.sql dblink--unpackaged--1.0.sql
+DATA = dblink--1.1.sql dblink--1.0--1.1.sql dblink--unpackaged--1.0.sql
REGRESS = dblink
diff --git a/contrib/dblink/dblink--1.0--1.1.sql b/contrib/dblink/dblink--1.0--1.1.sql
new file mode 100644
index 0000000..f224d3d
--- /dev/null
+++ b/contrib/dblink/dblink--1.0--1.1.sql
@@ -0,0 +1,14 @@
+/* contrib/dblink/dblink--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "ALTER EXTENSION dblink UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION dblink_fdw_validator(
+ options text[],
+ catalog oid
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'dblink_fdw_validator'
+LANGUAGE C IMMUTABLE;
+
+CREATE FOREIGN DATA WRAPPER dblink_fdw VALIDATOR dblink_fdw_validator;
diff --git a/contrib/dblink/dblink--1.0.sql b/contrib/dblink/dblink--1.0.sql
deleted file mode 100644
index 1fec9e3..0000000
--- a/contrib/dblink/dblink--1.0.sql
+++ /dev/null
@@ -1,223 +0,0 @@
-/* contrib/dblink/dblink--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION dblink" to load this file. \quit
-
--- dblink_connect now restricts non-superusers to password
--- authenticated connections
-CREATE FUNCTION dblink_connect (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_connect (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT;
-
--- dblink_connect_u allows non-superusers to use
--- non-password authenticated connections, but initially
--- privileges are revoked from public
-CREATE FUNCTION dblink_connect_u (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT SECURITY DEFINER;
-
-CREATE FUNCTION dblink_connect_u (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE C STRICT SECURITY DEFINER;
-
-REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
-REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public;
-
-CREATE FUNCTION dblink_disconnect ()
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_disconnect (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_open (text, text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, int)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, int, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, text, int)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_fetch (text, text, int, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_close (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, text)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, text, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink (text, boolean)
-RETURNS setof record
-AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text, text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text, text, boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_exec (text,boolean)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE C STRICT;
-
-CREATE TYPE dblink_pkey_results AS (position int, colname text);
-
-CREATE FUNCTION dblink_get_pkey (text)
-RETURNS setof dblink_pkey_results
-AS 'MODULE_PATHNAME','dblink_get_pkey'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_insert'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_delete'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text)
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_build_sql_update'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_current_query ()
-RETURNS text
-AS 'MODULE_PATHNAME','dblink_current_query'
-LANGUAGE C;
-
-CREATE FUNCTION dblink_send_query(text, text)
-RETURNS int4
-AS 'MODULE_PATHNAME', 'dblink_send_query'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_is_busy(text)
-RETURNS int4
-AS 'MODULE_PATHNAME', 'dblink_is_busy'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_result(text)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'dblink_get_result'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_result(text, bool)
-RETURNS SETOF record
-AS 'MODULE_PATHNAME', 'dblink_get_result'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_connections()
-RETURNS text[]
-AS 'MODULE_PATHNAME', 'dblink_get_connections'
-LANGUAGE C;
-
-CREATE FUNCTION dblink_cancel_query(text)
-RETURNS text
-AS 'MODULE_PATHNAME', 'dblink_cancel_query'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_error_message(text)
-RETURNS text
-AS 'MODULE_PATHNAME', 'dblink_error_message'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_notify(
- OUT notify_name TEXT,
- OUT be_pid INT4,
- OUT extra TEXT
-)
-RETURNS setof record
-AS 'MODULE_PATHNAME', 'dblink_get_notify'
-LANGUAGE C STRICT;
-
-CREATE FUNCTION dblink_get_notify(
- conname TEXT,
- OUT notify_name TEXT,
- OUT be_pid INT4,
- OUT extra TEXT
-)
-RETURNS setof record
-AS 'MODULE_PATHNAME', 'dblink_get_notify'
-LANGUAGE C STRICT;
diff --git a/contrib/dblink/dblink--1.1.sql b/contrib/dblink/dblink--1.1.sql
new file mode 100644
index 0000000..e23b5c9
--- /dev/null
+++ b/contrib/dblink/dblink--1.1.sql
@@ -0,0 +1,234 @@
+/* contrib/dblink/dblink--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION dblink" to load this file. \quit
+
+-- dblink_connect now restricts non-superusers to password
+-- authenticated connections
+CREATE FUNCTION dblink_connect (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_connect (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT;
+
+-- dblink_connect_u allows non-superusers to use
+-- non-password authenticated connections, but initially
+-- privileges are revoked from public
+CREATE FUNCTION dblink_connect_u (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+CREATE FUNCTION dblink_connect_u (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE C STRICT SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public;
+REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public;
+
+CREATE FUNCTION dblink_disconnect ()
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_disconnect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_disconnect (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_disconnect'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_open (text, text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, int)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, int, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, text, int)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_fetch (text, text, int, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_close (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, text)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, text, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink (text, boolean)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text, text, boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_exec (text,boolean)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE C STRICT;
+
+CREATE TYPE dblink_pkey_results AS (position int, colname text);
+
+CREATE FUNCTION dblink_get_pkey (text)
+RETURNS setof dblink_pkey_results
+AS 'MODULE_PATHNAME','dblink_get_pkey'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_insert'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_delete'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_build_sql_update'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_current_query ()
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_current_query'
+LANGUAGE C;
+
+CREATE FUNCTION dblink_send_query(text, text)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'dblink_send_query'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_is_busy(text)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'dblink_is_busy'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_result(text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'dblink_get_result'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_result(text, bool)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'dblink_get_result'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_connections()
+RETURNS text[]
+AS 'MODULE_PATHNAME', 'dblink_get_connections'
+LANGUAGE C;
+
+CREATE FUNCTION dblink_cancel_query(text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'dblink_cancel_query'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_error_message(text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'dblink_error_message'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_notify(
+ OUT notify_name TEXT,
+ OUT be_pid INT4,
+ OUT extra TEXT
+)
+RETURNS setof record
+AS 'MODULE_PATHNAME', 'dblink_get_notify'
+LANGUAGE C STRICT;
+
+CREATE FUNCTION dblink_get_notify(
+ conname TEXT,
+ OUT notify_name TEXT,
+ OUT be_pid INT4,
+ OUT extra TEXT
+)
+RETURNS setof record
+AS 'MODULE_PATHNAME', 'dblink_get_notify'
+LANGUAGE C STRICT;
+
+/* stuffs added since 1.1 */
+CREATE FUNCTION dblink_fdw_validator(
+ options text[],
+ catalog oid
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'dblink_fdw_validator'
+LANGUAGE C IMMUTABLE;
+
+CREATE FOREIGN DATA WRAPPER dblink_fdw VALIDATOR dblink_fdw_validator;
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 9f1dac8..6545cd2 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -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(PQconninfoOption *options,
+ const char *option, Oid context);
/* Global */
static remoteConn *pconn = NULL;
@@ -1912,6 +1917,76 @@ dblink_get_notify(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+/*
+ * Validate the generic option given to SERVER or USER MAPPING.
+ * Raise an ERROR if the option is considered invalid.
+ *
+ * We just validate the name of options here, so semantic errors of 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;
+ PQconninfoOption *options = NULL;
+
+ /*
+ * Get list of valid libpq options. It contains default values too, but we
+ * don't care the values. Obtained list is allocated with malloc, so we
+ * must free it before leaving this function.
+ */
+ options = PQconndefaults();
+ if (options == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FDW_OUT_OF_MEMORY),
+ errmsg("out of memory"),
+ errdetail("could not get libpq default connection options")));
+ PG_TRY();
+ {
+ /* Validate each supplied option. */
+ foreach(cell, options_list)
+ {
+ PQconninfoOption *opt;
+ DefElem *def = lfirst(cell);
+ StringInfoData buf;
+
+ if (!is_valid_dblink_option(options, def->defname, context))
+ {
+ /*
+ * Unknown option, or invalid option for the context specified,
+ * complain about it. Provide a hint with list of valid
+ * options for the object.
+ */
+ 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_CATCH();
+ {
+ free(options);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ free(options);
+ PG_RETURN_VOID();
+}
+
/*************************************************************
* internal functions
*/
@@ -2768,3 +2843,52 @@ validate_pkattnums(Relation rel,
errmsg("invalid attribute number %d", pkattnum)));
}
}
+
+/*
+ * Check if the provided option is one of valid dblink options. Valid libpq
+ * conninfo options are classified as below:
+ *
+ * every debug options : invalid in every context
+ * every secure options: valid in only USER MAPPING options
+ * "user" : valid in only USER MAPPING options
+ * Others : valid in both SERVER options and USER MAPPING options
+ *
+ * Note that we accept client_encoding as options, but it is always ignored
+ * because we override it with calling PQclientEncoding to ensure that proper
+ * conversion is used.
+ */
+static bool
+is_valid_dblink_option(PQconninfoOption *options, const char *option,
+ Oid context)
+{
+ PQconninfoOption *opt;
+
+ for (opt = options; opt->keyword; opt++)
+ if (strcmp(opt->keyword, option) == 0)
+ break;
+ if (opt->keyword == NULL)
+ return false;
+
+ /*
+ * All debug options "replication" is used in walreceiver.
+ */
+ if (strcmp(opt->dispchar, "D") == 0)
+ return false;
+
+ /*
+ * If the options is secure one or "user", it should be specified in only
+ * USER MAPPING options. Others should be specified in only SERVER options.
+ */
+ if (strcmp(opt->keyword, "user") == 0 || strcmp(opt->dispchar, "*") == 0)
+ {
+ if (context != UserMappingRelationId)
+ return false;
+ }
+ else
+ {
+ if (context != ForeignServerRelationId)
+ return false;
+ }
+
+ return true;
+}
diff --git a/contrib/dblink/dblink.control b/contrib/dblink/dblink.control
index 4333a9b..39f439a 100644
--- a/contrib/dblink/dblink.control
+++ b/contrib/dblink/dblink.control
@@ -1,5 +1,5 @@
# dblink extension
comment = 'connect to other PostgreSQL databases from within a database'
-default_version = '1.0'
+default_version = '1.1'
module_pathname = '$libdir/dblink'
relocatable = true
diff --git a/contrib/dblink/dblink.h b/contrib/dblink/dblink.h
index 935d283..fd11658 100644
--- a/contrib/dblink/dblink.h
+++ b/contrib/dblink/dblink.h
@@ -58,5 +58,6 @@ extern Datum dblink_build_sql_delete(PG_FUNCTION_ARGS);
extern Datum dblink_build_sql_update(PG_FUNCTION_ARGS);
extern Datum dblink_current_query(PG_FUNCTION_ARGS);
extern Datum dblink_get_notify(PG_FUNCTION_ARGS);
+extern Datum dblink_fdw_validator(PG_FUNCTION_ARGS);
#endif /* DBLINK_H */
diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out
index a1899ed..1cc06e3 100644
--- a/contrib/dblink/expected/dblink.out
+++ b/contrib/dblink/expected/dblink.out
@@ -783,8 +783,16 @@ SELECT dblink_disconnect('dtest1');
-- test foreign data wrapper functionality
CREATE USER dblink_regression_test;
-CREATE FOREIGN DATA WRAPPER postgresql;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (invalid 'val'); -- ERROR
+ERROR: invalid option "invalid"
+HINT: Valid options in this context are: service, connect_timeout, dbname, host, hostaddr, port, client_encoding, application_name, fallback_application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, requirepeer
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (password 'val'); -- ERROR
+ERROR: invalid option "password"
+HINT: Valid options in this context are: service, connect_timeout, dbname, host, hostaddr, port, client_encoding, application_name, fallback_application_name, keepalives, keepalives_idle, keepalives_interval, keepalives_count, sslmode, sslcompression, sslcert, sslkey, sslrootcert, sslcrl, requirepeer
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (server 'localhost'); -- ERROR
+ERROR: invalid option "server"
+HINT: Valid options in this context are: user, password
CREATE USER MAPPING FOR public SERVER fdtest;
GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
@@ -823,7 +831,6 @@ REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_t
DROP USER dblink_regression_test;
DROP USER MAPPING FOR public SERVER fdtest;
DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
-- test asynchronous notifications
SELECT dblink_connect('dbname=contrib_regression');
dblink_connect
diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql
index 8c8ffe2..171ee50 100644
--- a/contrib/dblink/sql/dblink.sql
+++ b/contrib/dblink/sql/dblink.sql
@@ -361,8 +361,10 @@ SELECT dblink_disconnect('dtest1');
-- test foreign data wrapper functionality
CREATE USER dblink_regression_test;
-CREATE FOREIGN DATA WRAPPER postgresql;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (invalid 'val'); -- ERROR
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (password 'val'); -- ERROR
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (dbname 'contrib_regression');
+CREATE USER MAPPING FOR public SERVER fdtest OPTIONS (server 'localhost'); -- ERROR
CREATE USER MAPPING FOR public SERVER fdtest;
GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
GRANT EXECUTE ON FUNCTION dblink_connect_u(text, text) TO dblink_regression_test;
@@ -381,7 +383,6 @@ REVOKE EXECUTE ON FUNCTION dblink_connect_u(text, text) FROM dblink_regression_t
DROP USER dblink_regression_test;
DROP USER MAPPING FOR public SERVER fdtest;
DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
-- test asynchronous notifications
SELECT dblink_connect('dbname=contrib_regression');
diff --git a/doc/src/sgml/dblink.sgml b/doc/src/sgml/dblink.sgml
index 72ca765..186ab86 100644
--- a/doc/src/sgml/dblink.sgml
+++ b/doc/src/sgml/dblink.sgml
@@ -46,12 +46,10 @@ dblink_connect(text connname, text connstr) returns text
The connection string may also be the name of an existing foreign
- server. It is recommended to use
- the postgresql_fdw_validator when defining
- the corresponding foreign-data wrapper. See the example below, as
- well as the following:
+ server. It is recommended to use the foreign-data wrapper
+ dblink_fdw when defining the corresponding foreign
+ server. See the example below, as well as the following:
-
@@ -136,8 +134,7 @@ SELECT dblink_connect('myconn', 'dbname=postgres');
-- DETAIL: Non-superuser cannot connect if the server does not request a password.
-- HINT: Target server's authentication method must be changed.
CREATE USER dblink_regression_test WITH PASSWORD 'secret';
-CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
-CREATE SERVER fdtest FOREIGN DATA WRAPPER postgresql OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
+CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');
CREATE USER MAPPING FOR dblink_regression_test SERVER fdtest OPTIONS (user 'dblink_regression_test', password 'secret');
GRANT USAGE ON FOREIGN SERVER fdtest TO dblink_regression_test;
@@ -173,7 +170,6 @@ REVOKE SELECT ON TABLE foo FROM dblink_regression_test;
DROP USER MAPPING FOR dblink_regression_test SERVER fdtest;
DROP USER dblink_regression_test;
DROP SERVER fdtest;
-DROP FOREIGN DATA WRAPPER postgresql;
diff --git a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
index 804fb47..d9936e8 100644
--- a/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
+++ b/doc/src/sgml/ref/create_foreign_data_wrapper.sgml
@@ -121,14 +121,6 @@ CREATE FOREIGN DATA WRAPPER name
There is no support for updating a foreign table, and optimization of
queries is primitive (and mostly left to the wrapper, too).
-
-
- There is one built-in foreign-data wrapper validator function
- provided:
- postgresql_fdw_validator, which accepts
- options corresponding to libpq> connection
- parameters.
-
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index d8845aa..8400db4 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -27,7 +27,6 @@
extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
-extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
/*
@@ -434,105 +433,6 @@ pg_options_to_table(PG_FUNCTION_ARGS)
/*
- * 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 && 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 == 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);
-}
-
-
-/*
* get_foreign_data_wrapper_oid - given a FDW name, look up the OID
*
* If missing_ok is false, throw an error if name not found. If true, just
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 77a3b41..67a488e8 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3433,9 +3433,6 @@ DESCR("filenode identifier of relation");
DATA(insert OID = 3034 ( pg_relation_filepath PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "2205" _null_ _null_ _null_ _null_ pg_relation_filepath _null_ _null_ _null_ ));
DESCR("file path of relation");
-DATA(insert OID = 2316 ( postgresql_fdw_validator PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "1009 26" _null_ _null_ _null_ _null_ postgresql_fdw_validator _null_ _null_ _null_));
-DESCR("(internal)");
-
DATA(insert OID = 2290 ( record_in PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "2275 26 23" _null_ _null_ _null_ _null_ record_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 2291 ( record_out PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2249" _null_ _null_ _null_ _null_ record_out _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 1bfdca1..0a5b525 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -13,6 +13,40 @@ CREATE ROLE regress_test_role2;
CREATE ROLE regress_test_role_super SUPERUSER;
CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
+-- create validator for this test
+CREATE OR REPLACE FUNCTION postgresql_fdw_validator(params text[], oid oid) RETURNS bool AS $$
+DECLARE
+ c text;
+ val text[];
+BEGIN
+ -- validate each option
+ FOR c IN SELECT unnest(params) LOOP
+ -- separate key and value
+ SELECT regexp_split_to_array(c, '=') INTO val;
+ RAISE DEBUG 'record %=%', val[1], val[2];
+
+ IF oid = 'pg_catalog.pg_foreign_server'::regclass THEN
+ IF val[1] NOT IN ('authtype', 'service', 'connect_timeout',
+ 'dbname', 'host', 'hostaddr', 'port', 'tty', 'options',
+ 'requiressl', 'sslmode', 'gsslib') THEN
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib';
+ END IF;
+ ELSIF oid = 'pg_catalog.pg_user_mapping'::regclass THEN
+ IF val[1] NOT IN ('user', 'password') THEN
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: user, password';
+ END IF;
+ ELSE
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: ';
+ END IF;
+
+ END LOOP;
+
+ RETURN true;
+END;
+$$ language plpgsql;
CREATE FOREIGN DATA WRAPPER dummy;
COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
@@ -1202,6 +1236,7 @@ DROP ROLE regress_test_role2;
DROP FOREIGN DATA WRAPPER postgresql CASCADE;
DROP FOREIGN DATA WRAPPER dummy CASCADE;
NOTICE: drop cascades to server s0
+DROP FUNCTION postgresql_fdw_validator(text[], oid);
\c
DROP ROLE foreign_data_user;
-- At this point we should have no wrappers, no servers, and no mappings.
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 38057db..46e358b 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -20,6 +20,41 @@ CREATE ROLE regress_test_role_super SUPERUSER;
CREATE ROLE regress_test_indirect;
CREATE ROLE unprivileged_role;
+-- create validator for this test
+CREATE OR REPLACE FUNCTION postgresql_fdw_validator(params text[], oid oid) RETURNS bool AS $$
+DECLARE
+ c text;
+ val text[];
+BEGIN
+ -- validate each option
+ FOR c IN SELECT unnest(params) LOOP
+ -- separate key and value
+ SELECT regexp_split_to_array(c, '=') INTO val;
+ RAISE DEBUG 'record %=%', val[1], val[2];
+
+ IF oid = 'pg_catalog.pg_foreign_server'::regclass THEN
+ IF val[1] NOT IN ('authtype', 'service', 'connect_timeout',
+ 'dbname', 'host', 'hostaddr', 'port', 'tty', 'options',
+ 'requiressl', 'sslmode', 'gsslib') THEN
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib';
+ END IF;
+ ELSIF oid = 'pg_catalog.pg_user_mapping'::regclass THEN
+ IF val[1] NOT IN ('user', 'password') THEN
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: user, password';
+ END IF;
+ ELSE
+ RAISE 'invalid option "%"', val[1]
+ USING HINT = 'Valid options in this context are: ';
+ END IF;
+
+ END LOOP;
+
+ RETURN true;
+END;
+$$ language plpgsql;
+
CREATE FOREIGN DATA WRAPPER dummy;
COMMENT ON FOREIGN DATA WRAPPER dummy IS 'useless';
CREATE FOREIGN DATA WRAPPER postgresql VALIDATOR postgresql_fdw_validator;
@@ -497,6 +532,7 @@ DROP ROLE unprivileged_role;
DROP ROLE regress_test_role2;
DROP FOREIGN DATA WRAPPER postgresql CASCADE;
DROP FOREIGN DATA WRAPPER dummy CASCADE;
+DROP FUNCTION postgresql_fdw_validator(text[], oid);
\c
DROP ROLE foreign_data_user;